mirror of https://github.com/aseprite/aseprite.git
Merge 35d209a4b1
into 1b16cfbe71
This commit is contained in:
commit
e6401db3ed
|
@ -1046,6 +1046,12 @@
|
||||||
<text color="slider_empty_text" align="center middle"/>
|
<text color="slider_empty_text" align="center middle"/>
|
||||||
<text color="slider_empty_text" align="center middle" state="focus" y="1"/>
|
<text color="slider_empty_text" align="center middle" state="focus" y="1"/>
|
||||||
</style>
|
</style>
|
||||||
|
<style id="textedit" border="2">
|
||||||
|
<background color="textbox_face" />
|
||||||
|
<text color="textbox_text" align="left" />
|
||||||
|
<text color="selected" align="left" state="selected" />
|
||||||
|
<text color="selected_text" align="left" state="selected" />
|
||||||
|
</style>
|
||||||
<style id="mini_slider" extends="slider" font="mini">
|
<style id="mini_slider" extends="slider" font="mini">
|
||||||
<background part="mini_slider_empty"/>
|
<background part="mini_slider_empty"/>
|
||||||
<text color="slider_empty_text" align="center middle"/>
|
<text color="slider_empty_text" align="center middle"/>
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
<gui>
|
<gui>
|
||||||
<window id="layer_properties" text="@.title">
|
<window id="layer_properties" text="@.title">
|
||||||
<vbox>
|
<vbox>
|
||||||
<grid id="properties_grid" columns="3">
|
<grid id="properties_grid" columns="3" expansive="true">
|
||||||
<label text="@.name" for="name" />
|
<label text="@.name" />
|
||||||
<entry text="" id="name" magnet="true" maxsize="256" minwidth="64" cell_align="horizontal" />
|
<entry text="" id="name" magnet="true" maxsize="256" minwidth="64" cell_align="horizontal" />
|
||||||
<button id="user_data" icon="icon_user_data" tooltip="@general.user_data" />
|
<button id="user_data" icon="icon_user_data" tooltip="@general.user_data" />
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
<label id="color_label" text="@.color" for="color" />
|
<label id="color_label" text="@.color" for="color" />
|
||||||
<label id="entry_label" text="@.user_data" for="entry" />
|
<label id="entry_label" text="@.user_data" for="entry" />
|
||||||
<colorpicker id="color" simple="true" expansive="true" />
|
<colorpicker id="color" simple="true" expansive="true" />
|
||||||
<entry id="entry" maxsize="65535" minwidth="128" expansive="true" />
|
<view id="text_edit_view" height="30" expansive="true">
|
||||||
|
<textedit id="text_edit" />
|
||||||
|
</view>
|
||||||
</hbox>
|
</hbox>
|
||||||
</gui>
|
</gui>
|
||||||
|
|
|
@ -344,7 +344,7 @@ private:
|
||||||
color_t c = m_cel->data()->userData().color();
|
color_t c = m_cel->data()->userData().color();
|
||||||
m_userDataView.color()->setColor(
|
m_userDataView.color()->setColor(
|
||||||
Color::fromRgb(rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c)));
|
Color::fromRgb(rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c)));
|
||||||
m_userDataView.entry()->setText(m_cel->data()->userData().text());
|
m_userDataView.textEdit()->setText(m_cel->data()->userData().text());
|
||||||
// Set last filled values in CelPropertiesWindow
|
// Set last filled values in CelPropertiesWindow
|
||||||
m_lastValues.opacity = m_cel->opacity();
|
m_lastValues.opacity = m_cel->opacity();
|
||||||
m_lastValues.zIndex = m_cel->zIndex();
|
m_lastValues.zIndex = m_cel->zIndex();
|
||||||
|
|
|
@ -465,7 +465,7 @@ private:
|
||||||
color_t c = m_layer->userData().color();
|
color_t c = m_layer->userData().color();
|
||||||
m_userDataView.color()->setColor(
|
m_userDataView.color()->setColor(
|
||||||
Color::fromRgb(rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c)));
|
Color::fromRgb(rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c)));
|
||||||
m_userDataView.entry()->setText(m_layer->userData().text());
|
m_userDataView.textEdit()->setText(m_layer->userData().text());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
name()->setText(Strings::layer_properties_no_layer());
|
name()->setText(Strings::layer_properties_no_layer());
|
||||||
|
|
|
@ -1233,6 +1233,8 @@ void SkinTheme::initWidget(Widget* widget)
|
||||||
widget->setStyle(styles.textboxText());
|
widget->setStyle(styles.textboxText());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case kTextEditWidget: widget->setStyle(styles.textedit()); break;
|
||||||
|
|
||||||
case kViewWidget:
|
case kViewWidget:
|
||||||
widget->setChildSpacing(0);
|
widget->setChildSpacing(0);
|
||||||
widget->setBgColor(colors.windowFace());
|
widget->setBgColor(colors.windowFace());
|
||||||
|
@ -1294,12 +1296,22 @@ int SkinTheme::getScrollbarSize()
|
||||||
return dimensions.scrollbarSize();
|
return dimensions.scrollbarSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx::Size SkinTheme::getEntryCaretSize(Widget* widget)
|
gfx::Size SkinTheme::getCaretSize(Widget* widget)
|
||||||
{
|
{
|
||||||
|
int caretHeight;
|
||||||
|
if (widget->type() == kTextEditWidget) {
|
||||||
|
// We cannot use the height of the widget text, because it
|
||||||
|
// includes the line height of every single line in the widget.
|
||||||
|
caretHeight = widget->font()->lineHeight();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
caretHeight = widget->textHeight();
|
||||||
|
}
|
||||||
|
|
||||||
if (widget->font()->type() == text::FontType::FreeType)
|
if (widget->font()->type() == text::FontType::FreeType)
|
||||||
return gfx::Size(2 * guiscale(), widget->textHeight());
|
return gfx::Size(2 * guiscale(), caretHeight);
|
||||||
else
|
else
|
||||||
return gfx::Size(2 * guiscale(), widget->textHeight() + 2 * guiscale());
|
return gfx::Size(2 * guiscale(), caretHeight + 2 * guiscale());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkinTheme::paintEntry(PaintEvent& ev)
|
void SkinTheme::paintEntry(PaintEvent& ev)
|
||||||
|
@ -1864,7 +1876,7 @@ void SkinTheme::drawEntryCaret(ui::Graphics* g, Entry* widget, int x, int y)
|
||||||
{
|
{
|
||||||
gfx::Color color = colors.text();
|
gfx::Color color = colors.text();
|
||||||
int textHeight = widget->textHeight();
|
int textHeight = widget->textHeight();
|
||||||
gfx::Size caretSize = getEntryCaretSize(widget);
|
gfx::Size caretSize = getCaretSize(widget);
|
||||||
|
|
||||||
for (int u = x; u < x + caretSize.w; ++u)
|
for (int u = x; u < x + caretSize.w; ++u)
|
||||||
g->drawVLine(color, u, y + textHeight / 2 - caretSize.h / 2, caretSize.h);
|
g->drawVLine(color, u, y + textHeight / 2 - caretSize.h / 2, caretSize.h);
|
||||||
|
|
|
@ -80,7 +80,7 @@ public:
|
||||||
void initWidget(ui::Widget* widget) override;
|
void initWidget(ui::Widget* widget) override;
|
||||||
void getWindowMask(ui::Widget* widget, gfx::Region& region) override;
|
void getWindowMask(ui::Widget* widget, gfx::Region& region) override;
|
||||||
int getScrollbarSize() override;
|
int getScrollbarSize() override;
|
||||||
gfx::Size getEntryCaretSize(ui::Widget* widget) override;
|
gfx::Size getCaretSize(ui::Widget* widget) override;
|
||||||
|
|
||||||
void paintEntry(ui::PaintEvent& ev) override;
|
void paintEntry(ui::PaintEvent& ev) override;
|
||||||
void paintListBox(ui::PaintEvent& ev) override;
|
void paintListBox(ui::PaintEvent& ev) override;
|
||||||
|
|
|
@ -90,10 +90,9 @@ SliceWindow::SliceWindow(const doc::Sprite* sprite,
|
||||||
entry->Change.connect([this, entry, mod] { onModifyField(entry, mod); });
|
entry->Change.connect([this, entry, mod] { onModifyField(entry, mod); });
|
||||||
}
|
}
|
||||||
|
|
||||||
ui::Entry* userDataEntry = m_userDataView.entry();
|
ui::TextEdit* userDataEntry = m_userDataView.textEdit();
|
||||||
userDataEntry->setSuffix("*");
|
// userDataEntry->setSuffix("*");
|
||||||
userDataEntry->Change.connect(
|
userDataEntry->Change.connect([this, userDataEntry] { onModifyField(nullptr, kUserData); });
|
||||||
[this, userDataEntry] { onModifyField(userDataEntry, kUserData); });
|
|
||||||
|
|
||||||
ColorButton* colorButton = m_userDataView.color();
|
ColorButton* colorButton = m_userDataView.color();
|
||||||
colorButton->Click.connect([this] { onPossibleColorChange(); });
|
colorButton->Click.connect([this] { onPossibleColorChange(); });
|
||||||
|
|
|
@ -58,14 +58,14 @@ void UserDataView::configureAndSet(const doc::UserData& userData, ui::Grid* pare
|
||||||
parent->addChildInCell(colorLabel(), hspan1, vspan, ui::LEFT);
|
parent->addChildInCell(colorLabel(), hspan1, vspan, ui::LEFT);
|
||||||
parent->addChildInCell(color(), hspan2, vspan, ui::HORIZONTAL);
|
parent->addChildInCell(color(), hspan2, vspan, ui::HORIZONTAL);
|
||||||
parent->addChildInCell(entryLabel(), hspan1, vspan, ui::LEFT);
|
parent->addChildInCell(entryLabel(), hspan1, vspan, ui::LEFT);
|
||||||
parent->addChildInCell(entry(), hspan2, vspan, ui::HORIZONTAL);
|
parent->addChildInCell(textEditView(), hspan2, vspan, ui::HORIZONTAL | ui::VERTICAL);
|
||||||
color()->Change.connect([this] { onColorChange(); });
|
color()->Change.connect([this] { onColorChange(); });
|
||||||
entry()->Change.connect([this] { onEntryChange(); });
|
textEdit()->Change.connect([this] { onEntryChange(); });
|
||||||
m_isConfigured = true;
|
m_isConfigured = true;
|
||||||
}
|
}
|
||||||
m_userData = userData;
|
m_userData = userData;
|
||||||
color()->setColor(Color::fromImage(doc::IMAGE_RGB, userData.color()));
|
color()->setColor(Color::fromImage(doc::IMAGE_RGB, userData.color()));
|
||||||
entry()->setText(m_userData.text());
|
textEdit()->setText(m_userData.text());
|
||||||
setVisible(isVisible());
|
setVisible(isVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,15 +79,15 @@ void UserDataView::setVisible(bool state, bool saveAsDefault)
|
||||||
colorLabel()->setVisible(state);
|
colorLabel()->setVisible(state);
|
||||||
color()->setVisible(state);
|
color()->setVisible(state);
|
||||||
entryLabel()->setVisible(state);
|
entryLabel()->setVisible(state);
|
||||||
entry()->setVisible(state);
|
textEditView()->setVisible(state);
|
||||||
if (saveAsDefault)
|
if (saveAsDefault)
|
||||||
m_visibility.setValue(state);
|
m_visibility.setValue(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserDataView::onEntryChange()
|
void UserDataView::onEntryChange()
|
||||||
{
|
{
|
||||||
if (entry()->text() != m_userData.text()) {
|
if (textEdit()->text() != m_userData.text()) {
|
||||||
m_userData.setText(entry()->text());
|
m_userData.setText(textEdit()->text());
|
||||||
if (!m_selfUpdate)
|
if (!m_selfUpdate)
|
||||||
UserDataChange();
|
UserDataChange();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@
|
||||||
#include "doc/user_data.h"
|
#include "doc/user_data.h"
|
||||||
#include "obs/signal.h"
|
#include "obs/signal.h"
|
||||||
#include "ui/base.h"
|
#include "ui/base.h"
|
||||||
#include "ui/entry.h"
|
|
||||||
#include "ui/grid.h"
|
#include "ui/grid.h"
|
||||||
#include "ui/label.h"
|
#include "ui/label.h"
|
||||||
|
#include "ui/textedit.h"
|
||||||
|
|
||||||
#include "user_data.xml.h"
|
#include "user_data.xml.h"
|
||||||
|
|
||||||
|
@ -31,7 +31,8 @@ public:
|
||||||
|
|
||||||
const doc::UserData& userData() const { return m_userData; }
|
const doc::UserData& userData() const { return m_userData; }
|
||||||
ColorButton* color() { return m_container.color(); }
|
ColorButton* color() { return m_container.color(); }
|
||||||
ui::Entry* entry() { return m_container.entry(); }
|
ui::TextEdit* textEdit() { return m_container.textEdit(); }
|
||||||
|
ui::View* textEditView() { return m_container.textEditView(); }
|
||||||
ui::Label* colorLabel() { return m_container.colorLabel(); }
|
ui::Label* colorLabel() { return m_container.colorLabel(); }
|
||||||
ui::Label* entryLabel() { return m_container.entryLabel(); }
|
ui::Label* entryLabel() { return m_container.entryLabel(); }
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "base/fs.h"
|
#include "base/fs.h"
|
||||||
#include "base/memory.h"
|
#include "base/memory.h"
|
||||||
#include "os/system.h"
|
#include "os/system.h"
|
||||||
|
#include "ui/textedit.h"
|
||||||
#include "ui/ui.h"
|
#include "ui/ui.h"
|
||||||
|
|
||||||
#include "tinyxml2.h"
|
#include "tinyxml2.h"
|
||||||
|
@ -259,13 +260,16 @@ Widget* WidgetLoader::convertXmlElementToWidget(const XMLElement* elem,
|
||||||
if (elem_name == "expr" && decimals)
|
if (elem_name == "expr" && decimals)
|
||||||
((ExprEntry*)widget)->setDecimals(strtol(decimals, nullptr, 10));
|
((ExprEntry*)widget)->setDecimals(strtol(decimals, nullptr, 10));
|
||||||
}
|
}
|
||||||
if (elem_name == "filename") {
|
else if (elem_name == "filename") {
|
||||||
const bool buttononly = bool_attr(elem, "buttononly", false);
|
const bool buttononly = bool_attr(elem, "buttononly", false);
|
||||||
const app::FilenameField::Type type = (buttononly ? app::FilenameField::Type::ButtonOnly :
|
const app::FilenameField::Type type = (buttononly ? app::FilenameField::Type::ButtonOnly :
|
||||||
app::FilenameField::Type::EntryAndButton);
|
app::FilenameField::Type::EntryAndButton);
|
||||||
|
|
||||||
widget = new app::FilenameField(type, "");
|
widget = new app::FilenameField(type, "");
|
||||||
}
|
}
|
||||||
|
else if (elem_name == "textedit") {
|
||||||
|
widget = new TextEdit();
|
||||||
|
}
|
||||||
else if (elem_name == "grid") {
|
else if (elem_name == "grid") {
|
||||||
const char* columns = elem->Attribute("columns");
|
const char* columns = elem->Attribute("columns");
|
||||||
bool same_width_columns = bool_attr(elem, "same_width_columns", false);
|
bool same_width_columns = bool_attr(elem, "same_width_columns", false);
|
||||||
|
|
|
@ -102,6 +102,8 @@ static Item convert_to_item(XMLElement* elem)
|
||||||
return item.typeIncl("app::DropDownButton", "app/ui/drop_down_button.h");
|
return item.typeIncl("app::DropDownButton", "app/ui/drop_down_button.h");
|
||||||
if (name == "entry")
|
if (name == "entry")
|
||||||
return item.typeIncl("ui::Entry", "ui/entry.h");
|
return item.typeIncl("ui::Entry", "ui/entry.h");
|
||||||
|
if (name == "textedit")
|
||||||
|
return item.typeIncl("ui::TextEdit", "ui/textedit.h");
|
||||||
if (name == "expr")
|
if (name == "expr")
|
||||||
return item.typeIncl("app::ExprEntry", "app/ui/expr_entry.h");
|
return item.typeIncl("app::ExprEntry", "app/ui/expr_entry.h");
|
||||||
if (name == "filename")
|
if (name == "filename")
|
||||||
|
|
|
@ -50,6 +50,7 @@ add_library(ui-lib
|
||||||
style.cpp
|
style.cpp
|
||||||
system.cpp
|
system.cpp
|
||||||
textbox.cpp
|
textbox.cpp
|
||||||
|
textedit.cpp
|
||||||
theme.cpp
|
theme.cpp
|
||||||
timer.cpp
|
timer.cpp
|
||||||
tooltips.cpp
|
tooltips.cpp
|
||||||
|
|
|
@ -139,7 +139,7 @@ gfx::Point Entry::caretPosOnScreen() const
|
||||||
|
|
||||||
void Entry::setCaretPos(const int pos)
|
void Entry::setCaretPos(const int pos)
|
||||||
{
|
{
|
||||||
gfx::Size caretSize = theme()->getEntryCaretSize(this);
|
gfx::Size caretSize = theme()->getCaretSize(this);
|
||||||
int textlen = lastCaretPos();
|
int textlen = lastCaretPos();
|
||||||
m_caret = std::clamp(pos, 0, textlen);
|
m_caret = std::clamp(pos, 0, textlen);
|
||||||
m_scroll = std::clamp(m_scroll, 0, textlen);
|
m_scroll = std::clamp(m_scroll, 0, textlen);
|
||||||
|
@ -521,7 +521,7 @@ gfx::Size Entry::sizeHintWithText(Entry* entry, const std::string& text)
|
||||||
{
|
{
|
||||||
const auto& font = entry->font();
|
const auto& font = entry->font();
|
||||||
|
|
||||||
int w = font->textLength(text) + +2 * entry->theme()->getEntryCaretSize(entry).w +
|
int w = font->textLength(text) + +2 * entry->theme()->getCaretSize(entry).w +
|
||||||
entry->border().width();
|
entry->border().width();
|
||||||
|
|
||||||
w = std::min(w, guiscale() * kMaxWidthHintForEntry);
|
w = std::min(w, guiscale() * kMaxWidthHintForEntry);
|
||||||
|
@ -546,7 +546,7 @@ void Entry::onSizeHint(SizeHintEvent& ev)
|
||||||
const auto& font = this->font();
|
const auto& font = this->font();
|
||||||
|
|
||||||
int trailing = font->textLength(getSuffix());
|
int trailing = font->textLength(getSuffix());
|
||||||
trailing = std::max(trailing, 2 * theme()->getEntryCaretSize(this).w);
|
trailing = std::max(trailing, 2 * theme()->getCaretSize(this).w);
|
||||||
|
|
||||||
int w = font->textLength("w") * std::min(m_maxsize, 6) + +trailing + border().width();
|
int w = font->textLength("w") * std::min(m_maxsize, 6) + +trailing + border().width();
|
||||||
|
|
||||||
|
@ -1012,7 +1012,7 @@ void Entry::recalcCharBoxes(const std::string& text)
|
||||||
box.codepoint = 0;
|
box.codepoint = 0;
|
||||||
box.from = box.to = lastTextIndex;
|
box.from = box.to = lastTextIndex;
|
||||||
box.x = lastX;
|
box.x = lastX;
|
||||||
box.width = theme()->getEntryCaretSize(this).w;
|
box.width = theme()->getCaretSize(this).w;
|
||||||
m_boxes.push_back(box);
|
m_boxes.push_back(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,7 @@ void IntEntry::onSizeHint(SizeHintEvent& ev)
|
||||||
{
|
{
|
||||||
const text::FontRef& font = this->font();
|
const text::FontRef& font = this->font();
|
||||||
int trailing = font->textLength(getSuffix());
|
int trailing = font->textLength(getSuffix());
|
||||||
trailing = std::max(trailing, 2 * theme()->getEntryCaretSize(this).w);
|
trailing = std::max(trailing, 2 * theme()->getCaretSize(this).w);
|
||||||
|
|
||||||
int min_w = font->textLength(m_slider->convertValueToText(m_min));
|
int min_w = font->textLength(m_slider->convertValueToText(m_min));
|
||||||
int max_w = font->textLength(m_slider->convertValueToText(m_max)) + trailing;
|
int max_w = font->textLength(m_slider->convertValueToText(m_max)) + trailing;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,208 @@
|
||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2024-2025 Igara Studio S.A.
|
||||||
|
// Copyright (C) 2001-2018 David Capello
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifndef UI_TEXT_EDIT_H_INCLUDED
|
||||||
|
#define UI_TEXT_EDIT_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "text/font_mgr.h"
|
||||||
|
#include "text/text_blob.h"
|
||||||
|
#include "ui/box.h"
|
||||||
|
#include "ui/theme.h"
|
||||||
|
#include "ui/view.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
using namespace text;
|
||||||
|
|
||||||
|
class TextEdit : public Widget,
|
||||||
|
public ViewableWidget {
|
||||||
|
public:
|
||||||
|
TextEdit();
|
||||||
|
|
||||||
|
void cut();
|
||||||
|
void copy();
|
||||||
|
void paste();
|
||||||
|
void selectAll();
|
||||||
|
|
||||||
|
obs::signal<void()> Change;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool onProcessMessage(Message* msg) override;
|
||||||
|
void onPaint(PaintEvent& ev) override;
|
||||||
|
void onInitTheme(InitThemeEvent& ev) override;
|
||||||
|
void onSizeHint(SizeHintEvent& ev) override;
|
||||||
|
void onScrollRegion(ScrollRegionEvent& ev) override;
|
||||||
|
void onSetText() override;
|
||||||
|
void onSetFont() override;
|
||||||
|
|
||||||
|
bool onKeyDown(const KeyMessage* keyMessage);
|
||||||
|
bool onMouseMove(const MouseMessage* mouseMessage);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Line {
|
||||||
|
std::string text;
|
||||||
|
std::vector<TextBlob::Utf8Range> utfSize;
|
||||||
|
int glyphCount = 0;
|
||||||
|
text::TextBlobRef blob;
|
||||||
|
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
|
||||||
|
// Line index for more convenient loops
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
void buildBlob(const Widget* forWidget);
|
||||||
|
|
||||||
|
// Insert text into this line based on a caret position, taking into account utf8 size.
|
||||||
|
void insertText(int pos, const std::string& str);
|
||||||
|
|
||||||
|
gfx::RectF getBounds(int glyph) const;
|
||||||
|
|
||||||
|
// Get the screen size between the start and end glyph positions.
|
||||||
|
gfx::RectF getBounds(int startGlyph, int endGlyph) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Caret {
|
||||||
|
explicit Caret(std::vector<Line>* lines = nullptr) : m_lines(lines) {}
|
||||||
|
explicit Caret(std::vector<Line>* lines, int line, int pos)
|
||||||
|
: m_line(line)
|
||||||
|
, m_pos(pos)
|
||||||
|
, m_lines(lines)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Caret(const Caret& caret) : m_line(caret.m_line), m_pos(caret.m_pos), m_lines(caret.m_lines) {}
|
||||||
|
|
||||||
|
int line() const { return m_line; }
|
||||||
|
int pos() const { return m_pos; }
|
||||||
|
|
||||||
|
void setPos(int pos);
|
||||||
|
void setLine(int line) { m_line = line; }
|
||||||
|
void set(int line, int pos);
|
||||||
|
|
||||||
|
bool left(bool byWord = false);
|
||||||
|
// Moves the position to the next word on the left, doesn't wrap around lines.
|
||||||
|
bool leftWord();
|
||||||
|
bool right(bool byWord = false);
|
||||||
|
// Moves the position to the next word on the right, doesn't wrap around lines.
|
||||||
|
bool rightWord();
|
||||||
|
void up();
|
||||||
|
void down();
|
||||||
|
bool isLastInLine() const { return m_pos == lineObj().glyphCount; }
|
||||||
|
bool isLastLine() const { return m_line == m_lines->size() - 1; }
|
||||||
|
|
||||||
|
// Go to the end of line.
|
||||||
|
void eol();
|
||||||
|
|
||||||
|
// Returns the absolute position of the caret, aka the position in the main string that has all
|
||||||
|
// the newlines.
|
||||||
|
int absolutePos() const;
|
||||||
|
bool isWordPart() const;
|
||||||
|
void advanceBy(int characters);
|
||||||
|
bool isValid() const;
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
bool operator==(const Caret& other) const
|
||||||
|
{
|
||||||
|
return m_line == other.m_line && m_pos == other.m_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Caret& other) const
|
||||||
|
{
|
||||||
|
return m_line != other.m_line || m_pos != other.m_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const Caret& other) const
|
||||||
|
{
|
||||||
|
if (m_line < other.m_line)
|
||||||
|
return true;
|
||||||
|
if (m_line > other.m_line)
|
||||||
|
return false;
|
||||||
|
return m_pos < other.m_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_line = 0;
|
||||||
|
int m_pos = 0;
|
||||||
|
std::string_view text() const { return (*m_lines)[m_line].text; }
|
||||||
|
Line& lineObj() const { return (*m_lines)[m_line]; }
|
||||||
|
std::vector<Line>* m_lines;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Selection {
|
||||||
|
Selection() = default;
|
||||||
|
Selection(const Caret& startCaret, const Caret& endCaret) { set(startCaret, endCaret); }
|
||||||
|
|
||||||
|
static Selection SelectWords(const Caret& from);
|
||||||
|
|
||||||
|
bool isEmpty() const
|
||||||
|
{
|
||||||
|
return (m_start.line() == m_end.line() && m_start.pos() == m_end.pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStart(const Caret& caret) { m_start = caret; }
|
||||||
|
void setEnd(const Caret& caret) { m_end = caret; }
|
||||||
|
void set(const Caret& startCaret, const Caret& endCaret);
|
||||||
|
|
||||||
|
const Caret& start() const { return m_start; }
|
||||||
|
const Caret& end() const { return m_end; }
|
||||||
|
bool isValid() const { return m_start.isValid() && m_end.isValid(); }
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
Selection& operator|=(const Selection& other)
|
||||||
|
{
|
||||||
|
m_start = std::min(m_start, other.start());
|
||||||
|
m_end = std::max(m_end, other.end());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Caret m_start;
|
||||||
|
Caret m_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the selection rect for the given line, if any
|
||||||
|
gfx::RectF getSelectionRect(const Line& line, const gfx::PointF& offset) const;
|
||||||
|
Caret caretFromPosition(const gfx::Point& position);
|
||||||
|
void showEditPopupMenu(const gfx::Point& position);
|
||||||
|
void insertCharacter(base::codepoint_t character);
|
||||||
|
void deleteSelection();
|
||||||
|
void ensureCaretVisible();
|
||||||
|
int maxHeight() const;
|
||||||
|
|
||||||
|
void startTimer();
|
||||||
|
void stopTimer();
|
||||||
|
|
||||||
|
Selection m_selection;
|
||||||
|
Selection m_selectionWords;
|
||||||
|
Caret m_caret;
|
||||||
|
Caret m_lockedSelectionStart;
|
||||||
|
|
||||||
|
std::vector<Line> m_lines;
|
||||||
|
|
||||||
|
// Whether or not we're currently drawing the caret, driven by a timer.
|
||||||
|
bool m_drawCaret = false;
|
||||||
|
|
||||||
|
// The last position the caret was drawn, to invalidate that region when repainting.
|
||||||
|
gfx::Rect m_caretRect;
|
||||||
|
|
||||||
|
// The total size of the complete text, calculated as the longest single line width and the sum of
|
||||||
|
// the total line heights
|
||||||
|
gfx::Size m_textSize;
|
||||||
|
|
||||||
|
// Color cache
|
||||||
|
gfx::Color m_colorBG;
|
||||||
|
gfx::Color m_colorSelected;
|
||||||
|
os::Paint m_textPaint;
|
||||||
|
os::Paint m_selectedTextPaint;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
|
||||||
|
#endif
|
|
@ -74,9 +74,10 @@ public:
|
||||||
virtual void getWindowMask(Widget* widget, gfx::Region& region) {}
|
virtual void getWindowMask(Widget* widget, gfx::Region& region) {}
|
||||||
virtual void setDecorativeWidgetBounds(Widget* widget);
|
virtual void setDecorativeWidgetBounds(Widget* widget);
|
||||||
virtual int getScrollbarSize() { return kDefaultFontHeight; }
|
virtual int getScrollbarSize() { return kDefaultFontHeight; }
|
||||||
virtual gfx::Size getEntryCaretSize(Widget* widget) { return gfx::Size(kDefaultFontHeight, 1); }
|
virtual gfx::Size getCaretSize(Widget* widget) { return gfx::Size(kDefaultFontHeight, 1); }
|
||||||
|
|
||||||
virtual void paintEntry(PaintEvent& ev) {}
|
virtual void paintEntry(PaintEvent& ev) {}
|
||||||
|
virtual void paintTextEdit(PaintEvent& ev) {}
|
||||||
virtual void paintListBox(PaintEvent& ev);
|
virtual void paintListBox(PaintEvent& ev);
|
||||||
virtual void paintMenu(PaintEvent& ev) {}
|
virtual void paintMenu(PaintEvent& ev) {}
|
||||||
virtual void paintMenuItem(PaintEvent& ev) {}
|
virtual void paintMenuItem(PaintEvent& ev) {}
|
||||||
|
|
|
@ -38,6 +38,7 @@ enum WidgetType : int {
|
||||||
kSliderWidget,
|
kSliderWidget,
|
||||||
kSplitterWidget,
|
kSplitterWidget,
|
||||||
kTextBoxWidget,
|
kTextBoxWidget,
|
||||||
|
kTextEditWidget,
|
||||||
kViewScrollbarWidget,
|
kViewScrollbarWidget,
|
||||||
kViewViewportWidget,
|
kViewViewportWidget,
|
||||||
kViewWidget,
|
kViewWidget,
|
||||||
|
|
Loading…
Reference in New Issue