[WIP] TextEdit Widget (implements #3131)#4968
Conversation
aseprite-bot
left a comment
There was a problem hiding this comment.
clang-tidy made some suggestions
| { | ||
| } | ||
|
|
||
| void FeedbackCommand::onExecute(Context* context) |
There was a problem hiding this comment.
warning: parameter 'context' is unused [misc-unused-parameters]
| void FeedbackCommand::onExecute(Context* context) | |
| void FeedbackCommand::onExecute(Context* /*context*/) |
| deleteSelection(); | ||
| } | ||
|
|
||
| void TextEdit::copy() |
There was a problem hiding this comment.
warning: method 'copy' can be made static [readability-convert-member-functions-to-static]
src/app/ui/textedit.h:27:
- void copy();
+ static void copy();| m_caret.advanceBy(clipboard.size()); | ||
| } | ||
|
|
||
| void TextEdit::selectAll() |
There was a problem hiding this comment.
warning: method 'selectAll' can be made static [readability-convert-member-functions-to-static]
src/app/ui/textedit.h:29:
- void selectAll();
+ static void selectAll();| return; | ||
|
|
||
| const Caret startCaret(&m_lines); | ||
| Caret endCaret(startCaret); |
There was a problem hiding this comment.
warning: variable 'endCaret' of type 'Caret' can be declared 'const' [misc-const-correctness]
Caret endCaret(startCaret);
^this fix will not be applied because it overlaps with another fix
|
|
||
| const Caret startCaret(&m_lines); | ||
| Caret endCaret(startCaret); | ||
| endCaret.set(m_lines.size() - 1, m_lines.back().glyphCount); |
There was a problem hiding this comment.
warning: local copy 'endCaret' of the variable 'startCaret' is never modified and never used; consider removing the statement [performance-unnecessary-copy-initialization]
| endCaret.set(m_lines.size() - 1, m_lines.back().glyphCount); | |
| endCaret.set(m_lines.size() - 1, m_lines.back().glyphCount); |
| m_pos = pos; | ||
| } | ||
|
|
||
| bool left(bool byWord = false) |
There was a problem hiding this comment.
warning: function 'left' is within a recursive call chain [misc-no-recursion]
bool left(bool byWord = false)
^Additional context
src/app/ui/textedit.h:210: example recursive call chain, starting from function 'leftWord'
bool leftWord()
^src/app/ui/textedit.h:217: Frame #1: function 'leftWord' calls function 'left' here:
if (!left())
^src/app/ui/textedit.h:192: Frame #2: function 'left' calls function 'leftWord' here:
return leftWord();
^src/app/ui/textedit.h:192: ... which was the starting point of the recursive call chain; there may be other cycles
return leftWord();
^| } | ||
|
|
||
| // Moves the position to the next word on the left, doesn't wrap around lines. | ||
| bool leftWord() |
There was a problem hiding this comment.
warning: function 'leftWord' is within a recursive call chain [misc-no-recursion]
bool leftWord()
^| return true; | ||
| } | ||
|
|
||
| bool right(bool byWord = false) |
There was a problem hiding this comment.
warning: function 'right' is within a recursive call chain [misc-no-recursion]
bool right(bool byWord = false)
^Additional context
src/app/ui/textedit.h:244: example recursive call chain, starting from function 'rightWord'
bool rightWord()
^src/app/ui/textedit.h:251: Frame #1: function 'rightWord' calls function 'right' here:
if (!right())
^src/app/ui/textedit.h:226: Frame #2: function 'right' calls function 'rightWord' here:
return rightWord();
^src/app/ui/textedit.h:226: ... which was the starting point of the recursive call chain; there may be other cycles
return rightWord();
^| } | ||
|
|
||
| // Moves the position to the next word on the right, doesn't wrap around lines. | ||
| bool rightWord() |
There was a problem hiding this comment.
warning: function 'rightWord' is within a recursive call chain [misc-no-recursion]
bool rightWord()
^|
|
||
| bool isWordPart(size_t pos) const | ||
| { | ||
| if (!lineObj().glyphCount || lineObj().utfSize.size() <= pos) |
There was a problem hiding this comment.
warning: implicit conversion 'size_t' (aka 'unsigned long') -> 'bool' [readability-implicit-bool-conversion]
| if (!lineObj().glyphCount || lineObj().utfSize.size() <= pos) | |
| if ((lineObj().glyphCount == 0u) || lineObj().utfSize.size() <= pos) |
|
I'll start reviewing this today, about:
I'd prefer to keep both widgets separated, as the single line The first thing I see is that the A problem with I'll continue reviewing the code as it is. |
| [textedit] | ||
| copy = &Copy | ||
| cut = Cu&t | ||
| paste = &Paste | ||
| select_all = Select &All |
There was a problem hiding this comment.
This is a good idea, I'll try to add these strings to en.ini and start using them in ui::Entry in some way (so then you can move TextEdit to the ui-lib).
Parts of this commit are from #4968 and will be useful to reuse in the multiline TextEdit widget. Co-authored-by: Christian Kaiser <info@ckaiser.com.ar>
|
Added a new ui::TranslationDelegate in bc3433f |
Parts of this commit are from #4968 and will be useful to reuse in the multiline TextEdit widget. Co-authored-by: Christian Kaiser <info@ckaiser.com.ar>
|
As the change is too big and I've already some local fixes, but cannot make a PR for this (as it includes temporal code, like the feedback command/dialog), I can cherry pick/integrate the changes from e572c83 to create the TextEdit widget and continue working from there. |
|
Other possibility is to adjust this PR in such a way to merge it, but removing all the Feedback stuff. Another note: Please follow the CODING_STYLE guide, mainly the one for switch/cases: Lines 83 to 87 in 3e3dd2a |
480bc0f to
4516885
Compare
4516885 to
261cb59
Compare
|
I'll be reviewing this one today. |
dacap
left a comment
There was a problem hiding this comment.
There is a lot to review yet, but probably it'd be better to fix these issues, create a good first commit, and I can send you PR to your branch to continue fixing issues from there, in other case I'll make a lot of comments here and probably it's better to just continue working together in one branch.
| virtual gfx::Color calcBgColor(const Widget* widget, const Style* style); | ||
| virtual gfx::Size calcMinSize(const Widget* widget, const Style* style); | ||
| virtual gfx::Size calcMaxSize(const Widget* widget, const Style* style); | ||
| virtual gfx::Color getColorById(const std::string& id) const { return gfx::ColorNone; }; |
There was a problem hiding this comment.
We should have a better way to use colors for the UI from the ui-lib side. Probably for ui::TextEdit we could have a ui::Style (the Widget style) for the text/face/bg, and an alternative ui::Style for the selected text (member of ui::TextEdit).
But ui-lib shouldn't know nothing about named colors/getting colors from an string ID.
There was a problem hiding this comment.
I wasn't exactly sure how to do this at first but then I had the brilliant idea of just accessing the theme layers manually to get the colors, which... I don't think any other widget does so I'm pretty sure it's not right but is there any example of ui-lib stuff having multiple styles and getting the colors that way? I'm assuming we can't count on the order of the layers to be always consistent across user themes but maybe that's a requirement and this is fine.
|
There are other changes in diff --git a/data/widgets/layer_properties.xml b/data/widgets/layer_properties.xml
index 6c4ef4115..2793f566f 100644
--- a/data/widgets/layer_properties.xml
+++ b/data/widgets/layer_properties.xml
@@ -4,7 +4,7 @@
<gui>
<window id="layer_properties" text="@.title">
<vbox>
- <grid id="properties_grid" columns="3">
+ <grid id="properties_grid" columns="3" expansive="true">
<label text="@.name" />
<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" />
diff --git a/data/widgets/slice_properties.xml b/data/widgets/slice_properties.xml
index a683919c5..549a86f1e 100644
--- a/data/widgets/slice_properties.xml
+++ b/data/widgets/slice_properties.xml
@@ -5,7 +5,7 @@
<gui>
<window id="slice_properties" text="@.title" help="slices#slice-properties">
<vbox>
- <grid id="properties_grid" columns="3">
+ <grid id="properties_grid" columns="3" expansive="true">
<label id="label1" text="@.name" />
<entry id="name" maxsize="256" magnet="true" cell_align="horizontal" expansive="true" />
<button id="user_data" icon="icon_user_data" maxsize="32" tooltip="@.user_data_tooltip" />
diff --git a/data/widgets/sprite_properties.xml b/data/widgets/sprite_properties.xml
index 19d4c5553..13fd1dd9e 100644
--- a/data/widgets/sprite_properties.xml
+++ b/data/widgets/sprite_properties.xml
@@ -4,7 +4,7 @@
<gui>
<window id="sprite_properties" text="@.title" help="sprite-properties">
<vbox>
- <grid id="properties_grid" columns="3">
+ <grid id="properties_grid" columns="3" expansive="true">
<label text="@.filename" />
<entry text="" id="name" maxsize="256" minwidth="64" readonly="true" cell_align="horizontal" />
<button id="user_data" icon="icon_user_data" maxsize="32" tooltip="@general.user_data" /> |
9fba37a to
815b8c8
Compare
815b8c8 to
846c9ca
Compare
aseprite-bot
left a comment
There was a problem hiding this comment.
clang-tidy made some suggestions
| // Check the line width and if we're more than halfway past the line, we can set the caret to | ||
| // the full line. | ||
| caret.setPos( | ||
| (offsetPosition.x > m_lines[caret.line()].width / 2) ? m_lines[caret.line()].glyphCount : 0); |
There was a problem hiding this comment.
warning: result of integer division used in a floating point context; possible loss of precision [bugprone-integer-division]
(offsetPosition.x > m_lines[caret.line()].width / 2) ? m_lines[caret.line()].glyphCount : 0);
^| } | ||
|
|
||
| for (int i = 0; i < run.glyphCount; ++i) { | ||
| gfx::RectF glyphBounds = run.getGlyphBounds(i).offset(gfx::PointF(0, lineStartY)); |
There was a problem hiding this comment.
warning: variable 'glyphBounds' of type 'gfx::RectF' (aka 'RectT') can be declared 'const' [misc-const-correctness]
| gfx::RectF glyphBounds = run.getGlyphBounds(i).offset(gfx::PointF(0, lineStartY)); | |
| gfx::RectF const glyphBounds = run.getGlyphBounds(i).offset(gfx::PointF(0, lineStartY)); |
| s_timer->start(); | ||
| } | ||
|
|
||
| void TextEdit::stopTimer() |
There was a problem hiding this comment.
warning: method 'stopTimer' can be made static [readability-convert-member-functions-to-static]
src/ui/textedit.h:160:
- void stopTimer();
+ static void stopTimer();|
|
||
| void commitRunBuffer(TextBlob::RunInfo& info) override | ||
| { | ||
| ASSERT(info.clusters == nullptr || *info.clusters == 0); |
There was a problem hiding this comment.
warning: boolean expression can be simplified by DeMorgan's theorem [readability-simplify-boolean-expr]
ASSERT(info.clusters == nullptr || *info.clusters == 0);
^Additional context
laf/base/debug.h:67: expanded from macro 'ASSERT'
if (!(condition)) { \
^|
|
||
| void TextEdit::Caret::setPos(size_t pos) | ||
| { | ||
| ASSERT(pos >= 0 && pos <= lineObj().glyphCount); |
There was a problem hiding this comment.
warning: boolean expression can be simplified by DeMorgan's theorem [readability-simplify-boolean-expr]
ASSERT(pos >= 0 && pos <= lineObj().glyphCount);
^Additional context
laf/base/debug.h:67: expanded from macro 'ASSERT'
if (!(condition)) { \
^| m_pos = pos; | ||
| } | ||
|
|
||
| bool TextEdit::Caret::left(bool byWord) |
There was a problem hiding this comment.
warning: function 'left' is within a recursive call chain [misc-no-recursion]
bool TextEdit::Caret::left(bool byWord)
^Additional context
src/ui/textedit.cpp:899: example recursive call chain, starting from function 'leftWord'
bool TextEdit::Caret::leftWord()
^src/ui/textedit.cpp:906: Frame #1: function 'leftWord' calls function 'left' here:
if (!left())
^src/ui/textedit.cpp:882: Frame #2: function 'left' calls function 'leftWord' here:
return leftWord();
^src/ui/textedit.cpp:882: ... which was the starting point of the recursive call chain; there may be other cycles
return leftWord();
^| return true; | ||
| } | ||
|
|
||
| bool TextEdit::Caret::leftWord() |
There was a problem hiding this comment.
warning: function 'leftWord' is within a recursive call chain [misc-no-recursion]
bool TextEdit::Caret::leftWord()
^| return true; | ||
| } | ||
|
|
||
| bool TextEdit::Caret::right(bool byWord) |
There was a problem hiding this comment.
warning: function 'right' is within a recursive call chain [misc-no-recursion]
bool TextEdit::Caret::right(bool byWord)
^Additional context
src/ui/textedit.cpp:932: example recursive call chain, starting from function 'rightWord'
bool TextEdit::Caret::rightWord()
^src/ui/textedit.cpp:939: Frame #1: function 'rightWord' calls function 'right' here:
if (!right())
^src/ui/textedit.cpp:915: Frame #2: function 'right' calls function 'rightWord' here:
return rightWord();
^src/ui/textedit.cpp:915: ... which was the starting point of the recursive call chain; there may be other cycles
return rightWord();
^| return true; | ||
| } | ||
|
|
||
| bool TextEdit::Caret::rightWord() |
There was a problem hiding this comment.
warning: function 'rightWord' is within a recursive call chain [misc-no-recursion]
bool TextEdit::Caret::rightWord()
^| void paste(); | ||
| void selectAll(); | ||
|
|
||
| obs::signal<void()> Change; |
There was a problem hiding this comment.
warning: member variable 'Change' has public visibility [misc-non-private-member-variables-in-classes]
obs::signal<void()> Change;
^|
I'll rebase this PR and continue this work in other branch/PR. |
|
We'll continue in #5455 (branch https://github.com/aseprite/aseprite/tree/multiple-lines-textbox ), I'll try to fix some problems with the selection with TTF fonts, etc. |

Supersedes #4743, implements #3131.
Uses some of the skeleton of features intended for the feedback dialog just as a way to test things quickly (Help -> Feedback opens an edit) - will be removed before merging.
This is a completely new widget designed from scratch, so in theory it could replace the other multiline and text entry widgets (would need to have a readonly and one-line toggles added, but it'd be pretty simple) - but for now I need more eyes on it because I've been churning at it for a million years.
Known issues: