Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Widgets/text area #4995

Merged
merged 22 commits into from
Nov 28, 2024
Merged

Widgets/text area #4995

merged 22 commits into from
Nov 28, 2024

Conversation

wiktor-obrebski
Copy link
Contributor

@wiktor-obrebski wiktor-obrebski commented Oct 8, 2024

This MR introduce TextArea widget.
This widgets is already in use in gui/journal and coming gui/notes tools.
Its provide much better support for text manipultation than TextField do.

Scripts journal has been migrated here:
DFHack/scripts#1327

@robob27
Copy link
Contributor

robob27 commented Oct 9, 2024

I really appreciate your work on all of this stuff! You really went all out with this widget and the result is fantastic. Journal is sooo good and it's awesome that every other script will have such a great textarea to work with. I have been looking forward to this PR 😄

@dhthwy
Copy link
Contributor

dhthwy commented Nov 1, 2024

This PR looks good to me.

My understanding is that this type of stuff (especially lua, ui) is primarily myk's area of responsibility to sign-off on, so it may take some time to be merged.

Copy link
Member

@myk002 myk002 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first pass review done (docs only)

docs/dev/Lua API.rst Show resolved Hide resolved
Comment on lines 5536 to 5540
* ``on_text_change``: Callback function called whenever the text changes.
The function signature should be ``on_text_change(new_text)``.

* ``on_cursor_change``: Callback function called whenever the cursor
position changes. The function signature should be ``on_cursor_change(new_cursor_pos)``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest also passing the old text/old cursor position so callbacks that care don't have to keep that state themselves. Callbacks that don't care don't even have to include the parameter in their function signatures.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will do it for the cursor, but I have doubts for it as default behaviour of on_text_change.
The text can be potentially long and storing and passing a copy of it on every keypress can be bad for performance and memory, especially that I assume most of the tools will not need it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done for on_text_change, also tests add.

Copy link
Member

@myk002 myk002 Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lua strings are immutable (a change to a string causes a new string to be allocated). If you take a reference to the string before modification and pass it to the callback along with the current (modified) string, there is no additional memory being used. There was already a string copy operation as a result of modifying the string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, as such - I am going to implement the old_text param.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done and tests added.

docs/dev/Lua API.rst Show resolved Hide resolved
position changes. The function signature should be ``on_cursor_change(new_cursor_pos)``.

* ``one_line_mode``: Boolean attribute that, when set to ``true``,
disables multi-line text features and restricts the text area to a single line.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should document whether string.char(10) will be interpreted as the cp437 glyph. It would be nice if it did, but that can be implemented later if it's not already true.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added.
\n will be removed from the text, not rendered as glyph.

Its not so obvious for me that they should be rendered as glyph, what do u think that?
maybe precise what do u mean by glyph, so I am sure we are think about the same thing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\n is ◙ in cp437 - It has a graphical representation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, in CP437, single-string multiline text isn't a thing. We're creating a funky hybrid here that needs careful documentation around the handling of string.char(10). You can use ◙ in a name, for example. Currently you cannot use that character at all in this widget (which is a perfectly fine compromise, I think), but if we are to use one_line_mode TextArea widgets to replace EditField, we'd probably want to support the whole CP437 character set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not something I feel comfortable to do here, but I am open to do it in a follow up PR. Is it fine?

docs/dev/Lua API.rst Show resolved Hide resolved
docs/dev/Lua API.rst Show resolved Hide resolved

Scrolls the text area view to ensure that the current cursor position is visible.
This is useful for automatically scrolling when the user moves the cursor
beyond the visible region of the text area.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scrolling on cursor movement doesn't happen automatically? I'd expect this to be the default so the parent isn't required to implement on_cursor_change for a good default behavior. What are the use cases for on_cursor_change callback other than to call this function? If we had that "keep cursor visible when moving cursor" default functionality, are there any other use cases for this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we had that "keep cursor visible when moving cursor" default functionality, are there any other use cases for this function?

It is default functionality for all default manipulation of TextArea by the user.

But there is :setCursor API where we allow the API consumer to manipulate the cursor.
This function do not automatically scroll to the cursor, to allow API consumer to better control.

e.g. imagine someone want to paste a text to end of the textarea for some reason, but do not change user cursor and scroll position.

I had a similar case with gui/journal Table of Contents feature, but I do not remember now what it was exactly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think I understand. How about this?

     Scrolls the text area view to ensure that the current cursor position is visible.
-    This is useful for automatically scrolling when the user moves the cursor
-    beyond the visible region of the text area.
+    This happens automatically when the user interactively moves the cursor or
+    pastes text into the widget, but may need to be called when ``setCursor`` is
+    called programmatically.

Comment on lines 5600 to 5601
- Text Selection: Select text with the mouse, with support for replacing or
removing selected text.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be moved above Ctrl-U so mouse users know they can skip paying attention to the deletion keyboard shortcuts. Actually, on second thought, I'd split this list into two sections:

1st section: baseline functionality (features that players would expect from any text editor: text wrapping, mouse selection, copy/paste, undo/redo). We can probably reduce the amount of documentation dedicated to well-understood features, like Home and End. You can just mention that "the usual keyboard keys are supported, like Home, End, Backspace, Delete, etc. In addition, common mouse gestures are supported, like double click to highlight a word or triple click to highlight a line."

2nd section: keyboard power user shortcuts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not convinced, IMO we should keep all option described.
its quite think line what is "baseline" features and what now, it can be different for every user.

may proposition is to include and intro, like u proposed, but below it, in dedicated section, keep all shortcuts like now.
what do u think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at least Text Selection in particular should be moved adjacent to the Mouse Control line so similar concepts are grouped. This list is long and feels unorganized

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improved, what do u think about current version?

- Jump to Beginning/End: Quickly move the cursor to the beginning or end of the
text using :kbd:`Ctrl` + :kbd:`Home` and :kbd:`Ctrl` + :kbd:`End`.
- Select Word/Line: Use double click to select current word, or triple click to
select current line
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing final period (also true for sentences below)

Also add tests for TextArea `on_cursor_change` callback
Copy link
Member

@myk002 myk002 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a line to the Lua section of the changelog.

To allow me to see the diff between reviews, could you keep the commits without force pushes? Force pushing causes GitHub to lose the review context.

The cursor in the ``TextArea`` class is index-based, starting from 1,
consistent with Lua's text indexing conventions.

Each character, including new lines (``string.char(10)``),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Each character, including new lines (``string.char(10)``),
Each character, including newlines (``string.char(10)``),

* ``init_text``: The initial text content for the text area.

* ``init_cursor``: The initial cursor position within the text content.
If not specified, defaults to end of the text (length of ``init_text``).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

length of init_text plus one, right? (according to the Cursor Behavior text above)

Comment on lines 5550 to 5552
* ``text_pen``: Optional pen used to draw the text.

* ``select_pen``: Optional pen used for text selection.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document default pens

Comment on lines 5565 to 5566
If any "\n" (``string.char(10)``) are available in the text,
they will be removed from the text
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this phrasing a unclear for the semantics I expect. This is the behavior I'd like to see described:

  • If multiline text is pasted into the widget, newlines are removed (that is, the non-multiline SDL clipboard functions are used)
  • The Enter key is not handled by the widget as if it were included in ignore_keys


Scrolls the text area view to ensure that the current cursor position is visible.
This is useful for automatically scrolling when the user moves the cursor
beyond the visible region of the text area.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think I understand. How about this?

     Scrolls the text area view to ensure that the current cursor position is visible.
-    This is useful for automatically scrolling when the user moves the cursor
-    beyond the visible region of the text area.
+    This happens automatically when the user interactively moves the cursor or
+    pastes text into the widget, but may need to be called when ``setCursor`` is
+    called programmatically.

TextAreaContent.ATTRS{
text = '',
text_pen = COLOR_LIGHTCYAN,
ignore_keys = {'STRING_A096'},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as above -- I think we can change the default to an empty list due to INTERCEPT_HANDLED_HOTKEYS

enable_cursor_blink = true,
debug = false,
one_line_mode = false,
history_size = 10,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the internet does not seem to have a good consensus of what a 'good' default history size is, but 10 seems low to me. How about 20 (or leave the attribute as DEFAULT_NIL and use the HistoryStore default of 25)?

Comment on lines 66 to 68
function TextAreaContent:getPreferredFocusState()
return true
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't make sense for both TextArea and TextAreaContent to want focus since TextAreaContent is a child of TextArea. This function should be removed since it is TextArea that really needs the focus.


function WrappedText:update(text, wrap_width)
self.lines = text:wrap(
wrap_width,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if wrap_width is nil, then the text will be wrapped at 72 characters. Is this appropriate? Should wrap_width instead default to math.huge?


config.target = 'core'

local df_major_version = tonumber(dfhack.getCompiledDFVersion():match('%d+'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now that 50.14 has been released (which supports the new cursor navigation keybindings), you can take this out and run the tests unconditionally

@dhthwy
Copy link
Contributor

dhthwy commented Nov 21, 2024

You force pushed him again. lol.

@myk002
Copy link
Member

myk002 commented Nov 22, 2024

I should clarify that force pushing is ok to amend a just-committed commit. The review complications happen when force pushing such that already-reviewed commits get a new hash.

they will be removed from the text
* ``one_line_mode``: If set to ``true``, disables multi-line text features.
In this mode the :kbd:`Enter` key is not handled by the widget
as if it were included in ``ignore_keys``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
as if it were included in ``ignore_keys``.
as if it were included in ``ignore_keys``.


Functionality:
Functionality
-------------
Copy link
Member

@myk002 myk002 Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

welcome to the wonderful world of restructured text header underline rules. Currently, this section is interpreted like this:
image
that is, Functionality is a peer of TextArea class and Cursor Behavior is a higher level header than TextArea class.

You need to scan through the existing headers and discover what underline style is used for what level of header. It's different for every file, and is determined by whatever the file uses first for that particular header depth.

The indentation for list continuation lines is also apparently different from what sphinx expects:
image

You can see how this PR's text is currently rendered here: https://dfhack--4995.org.readthedocs.build/en/4995/docs/dev/Lua%20API.html#textarea-class

You can get to that link by clicking on "details" for the ReadTheDocs build action:
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank for the tip, I did not know that I can preview docs in build results!
I corrected the docs now.

- New Lines: Easily insert new lines using the :kbd:`Enter` key, supporting
multiline text input.
- Text Wrapping: Text automatically wraps within the editor, ensuring lines fit
within the display without manual adjustments.
- Scrolling behaviour for long text build-in.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Scrolling behaviour for long text build-in.
- Scrolling for long text entries.

end
end

function TextArea:getPreferredFocusState()
return self.parent_view.focus
return self.parent_view and self.parent_view.focus or true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just true?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read the docs, but I was always confused by this "preferred focus" feature.
Changed to true

@myk002 myk002 merged commit 3aeb7e2 into DFHack:develop Nov 28, 2024
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants