This feature allows any application to easily install an automatically
updating list of recently open files in a GUI::Menu.
There are three main pieces to this mechanism:
- GUI::Application::set_config_domain(domain): This must be called
before using the recent files feature. It informs the Application
object about which config domain to find the relevant RecentFiles
list under.
- GUI::Menu::add_recently_open_files(callback): This inserts the list
in a menu. A callback must be provided to handle actually opening
the recent file in some application-specific way.
- GUI::Application::set_most_recently_open_file(path): This updates
the list of recently open files, both in the configuration files,
and in the GUI menu.
Each {} block is now treated as a folding region, so that they can be
collapsed and expanded in TextEditor, GML Playground, HackStudio, and
anywhere else that uses the syntax highlighter. :^)
Previously this stored the position of each visual line break, meaning
that all the text would always be painted. By storing each visual
line's Utf32View, we can skip over parts of the text, such as for code
folding.
This only becomes a problem with folding, since arbitrary lines may be
invisible, meaning we try to apply a span for an invisible line N, on
line N+X instead, causing occasional crashes.
This check means we can remove the loop that skips spans occurring at
the end of the line.
We are currently setting the physical mouse position to the visual
cursor content location. In a line containing only a multi-code point
emoji, this would set the cursor to column 1 rather than however many
code points there are.
Nobody made use of the ErrorOr return value and it just added more
chance of confusion, since it was not clear if failing to sniff an
image should return an error or false. The answer was false, if you
returned Error you'd crash the ImageDecoder.
Changed the Action destructor to fix an issue where global scoped
actions without shortcuts were not being properly unregistered from the
application. Previously, this could cause crashes when attempting to
open the command palette.
We will currently only wrap "words" at ASCII spaces and, when wrapping,
will break up multi-code point emoji. This changes word wrapping to
behave as follows:
When the wrapping mode is "anywhere", use the iterator-based font width
computation overload. This will compute the width of multi-code point
emoji, whereas we currently only handle single-code point.
When the wrapping mode is "word", use the Unicode word segmentation
boundaries to break lines.
When clicking a position within a TextEditor, we should interpret that
position as a visual location. That location should be converted to a
"physical" location before using it to set the physical cursor position.
For example, consider a document with 2 emoji, each consisting of 3 code
points. Visually, these will occupy 2 columns. When a mouse click occurs
between these columns, we need to convert the visual column number 1 to
the physical column number 3 when storing the new cursor location.
TextDocument::merge_span_collections() automatically makes sure that the
spans are valid, move forwards, are in order, and do not overlap. This
means we don't have to check these things every time TextEditor paints
the document.
merge_span_collections() now does these checks instead. I am not certain
they are still useful, but someone in the past certainly did. I have
modified them to take advantage of the operator overloads and Formatter
that we now have.
We were manually adding together the gutter and ruler widths in several
places. Soon we'll have a third section that needs to be included in
this width, so let's abstract it now.
- Make gutter/ruler_content_rect() return rectangles relative to the
TextEditor widget.
- Re-order painting code to translate the Painter after the gutter/ruler
has been painted, to use those coordinates.
- Consistently put gutter before ruler in code, because that's the order
they physically appear.
When viewing a deeply nested path, there may be very little of the
PathBreadcrumbbar itself visible to double-click on. This solves that
by allowing double-clicks on its segment buttons to behave the same.
(With the caveat that it first selects the double-clicked segment.)
In order to make this work, `on_double_click` now takes the modifiers
instead of the MouseEvent. In this case we don't use it so that's fine,
but maybe we should make all mouse callbacks consistently take the
MouseEvent& as a parameter.
This Widget wraps both a Breadcrumbbar and a TextBox for editing the
path manually, based heavily on the existing code in FileManager.
Breadcrumbbar itself requires outside code to micro-manage what it does.
This class provides a simpler interface for it: Users don't have to
worry about segments, they just give/receive a string for the current
path.
This just calls Layout::try_add_spacer(), but saves you having to access
the Widget's Layout directly.
We verify that the Widget has a Layout, since it would be a programming
error if we tried to do so without one.
Currently, if you use the left/right arrow keys to move over a multi-
code point glyph, we will move through that glyph one code point at a
time. This means you can "pause" your movement in the middle of a glyph
and delete a subsection of a grapheme cluster. This now moves the cursor
across the entire cluster.
Visually, we will need to separately track physical and virtual cursor
positions. That is, when you move across a multi-code point glyph, the
visual cursor should only move one position at a time, while a physical
cursor stores the "real" position in terms of number of code points.
This also converts a couple of ints to auto - these are actually size_t,
and are being passed to functions that expect size_t, so let's not cast
them to ints.
Updated TextDocument and TextEditor to use calls to
`find_grapheme_segmentation_boundary` in order to make "correct-feeling"
deletions on backspace and delete keys being pressed
Note that for traversing words with ctrl+(left or right arrow) on the
keyboard, we want to "skip" some boundaries. Consider the text:
The ("quick") fox can't jump 32.3 feet, right?
Using "|" to denote word break boundaries, we will have:
|The| |(|"|quick|"|)| |fox| |can't| |jump| |32.3| |feet|,| |right|?|
When starting at "The" and using ctrl+right to move rightward in this
text, it is unlikely that users will want to break at every single one
of those boundaries. Most text editors, for example, will skip from the
end of "The" to the end of "quick", not breaking at the parentheses or
opening quotation marks.
We try to mimic such desired behavior here. It likely isn't perfect, but
we can improve upon it as we find edge cases.