Commit graph

58 commits

Author SHA1 Message Date
Andreas Kling
27a30fdc2a GTextEditor: Allow moving the cursor span-wise with Ctrl+Left/Right
This allows you to move token by token when using HackStudio to edit
a C++ file.

Fixes #767.
2019-11-15 21:02:24 +01:00
ctfloyd
a3520bfdfd GTextEditor: Alt+Shift+S alphabetically sorts selected lines 2019-11-15 21:01:12 +01:00
Andreas Kling
ca538b6cee LibGUI: Add a simple GWidget class registry/factory
You can now register a GWidget subclass with REGISTER_GWIDGET(class)
and it will be available for factory construction through the new
GWidgetClassRegistration interface.

To obtain a GWidgetClassRegistration for a given class name, you call
GWidgetClassRegistration::find(class_name). You can also iterate over
all the registered classes using GWCR::for_each(callback).

This will be very useful for implementing a proper GUI designer, and
also in the future for things like script bindings.

NOTE: All of the registrations are done in GWidget.cpp at the moment
since I ran into trouble with the fricken linker pruning the global
constructors this mechanism relies on. :^)
2019-11-10 12:57:37 +01:00
Rhin
503fe37eaa TextEditor: Enable/disable undo & redo buttons based on availability (#740) 2019-11-09 08:50:39 +01:00
Andreas Kling
fa77a57257 GTextEditor: Allow moving the selected line(s) up/down in bulk
You can now press Ctrl+Shift+Up/Down in a GTextEditor and the currently
selected line(s) will all move together one step up/down.

If there is no selection, we move the line with the cursor on it. :^)
2019-11-08 20:14:42 +01:00
Andreas Kling
c16b1a515e GTextEditor: Add a way to flush any pending on_change notifications
Since on_change handlers can alter the text document we're working on,
we have to make sure they've been run before we try looking at spans.
This fixes some flakiness when a paint happened before HackStudio had
a chance to re-highlight some C++ while editing it.

The design where clients of GTextEditor perform syntax highlighting in
the "arbitrary code execution" on_change callback is not very good.
We should find a way to move highlighting closer to the editor.
2019-11-08 19:51:10 +01:00
rhin123
96c1e36ed3 TextEditor: Added redo functionality & proper stack manipulation
Added redo functionality & added m_undo_stack_index that moves back &
forth on the stack depending on the undo's & redo's.
2019-11-07 22:29:59 +01:00
Andreas Kling
f5c2ae1192 GTextEditor: Move the cursor to column 0 after deleting a whole line 2019-11-04 12:36:10 +01:00
rhin123
9e608885d1 TextEditor: Added undo functionality
Created a stack where a vector of undo actions are stored.
2019-11-03 20:21:14 +01:00
Andreas Kling
b8bf998b61 LibGUI: Move text search functions from GTextEditor to GTextDocument
Also add a find_all() that retuns a Vector<GTextRange> and simply does
a find_next() loop, returning all the matching ranges.
2019-11-01 21:31:06 +01:00
Andreas Kling
bddba567b3 LibGUI: Add GTextDocument::text_in_range(GTextRange)
This function returns a String containing the text in a given range.
GTextEditor::selected_text() is now just a wrapper around this.
2019-10-30 20:28:44 +01:00
Andreas Kling
9b13a3905b LibGUI: Support multiple GTextEditors editing the same GTextDocument
With this patch, you can now assign the same GTextDocument to multiple
GTextEditor widgets via GTextEditor::set_document().

The editors have independent cursors and selection, but all changes
are shared, and immediately propagate to all editors.

This is very unoptimized and will do lots of unnecessary invalidation,
especially line re-wrapping and repainting over and over again.
2019-10-27 19:36:59 +01:00
Andreas Kling
f96c683543 LibGUI: Move visual line metadata from GTextDocument to GTextEditor
This patch decouples GTextDocument and GTextDocumentLine from the line
wrapping functionality of GTextEditor.

This should basically make it possible to have multiple GTextEditors
editing the same GTextDocument. Of course, that will require a bit more
work since there's no paint invalidation yet.
2019-10-27 18:00:07 +01:00
Andreas Kling
f1c6193d6d LibGUI: Move GTextDocument out of GTextEditor
The idea here is to decouple the document from the editor widget so you
could have multiple editors being views onto the same document.

This doesn't work yet, since the document and editor are coupled in
various ways still (including a per-line back-pointer to the editor.)
2019-10-27 16:44:16 +01:00
Andreas Kling
db5178fb8f GTextEditor: Double-clicking on a span should select the span
This makes double-clicking on a C++ token in HackStudio select the
whole token, which is pretty nice. It's not perfect in all cases,
but a lot nicer than just expanding until we hit whitespace. :^)
2019-10-27 11:18:19 +01:00
Andreas Kling
8fa466e496 GTextEditor: Backspace over soft tabs
This makes the backspace erase backwards until the next soft tab stop.
We currently always use 4 as the soft tab width, but I suppose we could
make it configurable at some point. :^)
2019-10-27 10:42:48 +01:00
Andreas Kling
b513a787fb GTextEditor: set_text() should clear any existing spans 2019-10-26 20:21:12 +02:00
Andreas Kling
bc2026d26d LibGUI: Make GTextEditor::Span have a range instead of two positions
A GTextRange is really just two GTextPositions (start and end) anyway.
This way we can say nice things like "if (range.contains(position))"
2019-10-26 15:33:19 +02:00
Andreas Kling
4fa8acf6ea GTextEditor: The Home key should jump to the first non-space character
Press Home twice to get to column 0. This feels way more natural.
2019-10-26 14:02:39 +02:00
Andreas Kling
532001f4c1 GTextEditor: Arrow keys should only modify selection when Shift is held 2019-10-26 14:01:53 +02:00
Andreas Kling
59107a7cfe GTextEditor: Allow setting a custom font for each span 2019-10-26 00:13:07 +02:00
Andreas Kling
0d53d74d5f GTextEditor: Add a "span" mechanism for having custom-style text ranges
It's now possible to give GTextEditor a vector of Span objects.
Spans currently tell the editor which color to use for each character
in the span. This can be used to implement syntax highlighting :^)
2019-10-25 21:07:02 +02:00
Andreas Kling
0a0dfeee8b LibGUI: Make GTextEditor::set_cursor() public
Also clamp the cursor value to the possible range instead of asserting
when trying to set a cursor past the end of the document.
2019-10-21 19:01:27 +02:00
Andreas Kling
caf1b37e75 GTextEditor: Unbreak right-aligned single-line text boxes
This makes the Calculator app look right once again! :^)
2019-09-16 20:57:32 +02:00
Andreas Kling
b41b5433f4 LibGUI: Add Undo/Redo to GCommonActions 2019-09-14 22:23:49 +02:00
Andreas Kling
e83390387c LibGUI: Simplify GCommonActions a bit
Use the same callback signature as GAction so we can just forward it
to GAction instead of chaining callbacks.
2019-09-14 22:10:44 +02:00
Andreas Kling
11f2e7cd5c GMenu: Update apps now that you can create a nameless GMenu
We had many context menus with names, simply because you were forced
to give them names.
2019-09-13 22:14:07 +02:00
Andreas Kling
6ab498edf7 GTextEditor: Paint line numbers with TopRight text alignment
This makes sure they line up with the first visual line for wrapped
lines that span multiple visual lines.
2019-09-06 19:24:16 +02:00
rhin123
1adec6d54b TextEditor: Removed unnecessary use of for_each
Didn't notice that m_visual_rect existed :P
2019-09-06 07:17:57 +02:00
rhin123
5594f19624 TextEditor: Added GCommonActions 2019-09-05 09:40:54 +02:00
Andreas Kling
77a58119e7 GTextEditor: Hide the horizontal scrollbar when line-wrapping is on 2019-09-01 20:34:50 +02:00
Andreas Kling
906582d8df GTextEditor: Fix wrong width calculations with line-wrapping enabled
There were various little mistakes in the width calculations used by
the line-wrapping layout code.

With this patch, we should no longer see the horizontal scrollbar get
enabled with line-wrapping enabled. I will hide the scrollbar in a
separate patch.
2019-09-01 20:04:25 +02:00
Andreas Kling
74ca299b4b GTextEditor: Make visual lines stop after their last character
Instead of letting each visual line run to the end of the editor when
wrapping lines, stop each visual line where it runs ouf characters.

Fixes #493.
2019-09-01 17:30:23 +02:00
Rhin
3e6a0a0533 TextEditor: Stopped disappearing text at end of document (#505)
text_position_at() was returning -1 if the position wasn't in
the bounds of a visual line. Now if the position is past the last
line, we simply return the last line index instead of -1.

Fixes #502.
2019-09-01 12:34:14 +02:00
Andreas Kling
3e2e086011 LibGUI: Add a way for GWidget subclasses to learn that the font changed
Use this in GTextEditor to update the vertical scrolling step size so
we always scroll one-line-at-a-time.
2019-09-01 12:26:35 +02:00
Rhin
d3ebd8897f GTextEditor: Set content size based on the visual line rects (#500)
When we update our content size, the width & height is now calculated
from the visual line rect size. Also now after we recompute all visual
lines, if the total height is different, we re-update the content size.
2019-08-29 06:26:24 +02:00
Andreas Kling
50ef2216fa GTextEditor: Optimize write_to_file() with ftruncate()
Compute the final file size and ftruncate() the destination file to the
right size immediately instead of incrementally appending to it.

This kind of optimization belongs in the kernel, but until we have it
there, this makes saving text files a whole lot faster. :^)
2019-08-28 19:32:45 +02:00
Andreas Kling
fcf85d8fef GTextEditor: Always call did_change() after deleting with backspace
This is needed for the on_change callback to fire.
2019-08-27 20:16:52 +02:00
Andrew Weller
ed0553fe10 GTextEditor: Fixed bug on KeyCode::Key_Right pressed.
Pressing right did nothing when the very last characters of the buffer
were selected.

The expected action would be for the cursor to move to the end of the
buffer.

This patch fixes that.
2019-08-27 07:17:22 +02:00
Andrew Weller
b50ffaf7e1 GTextEditor: Fixed bug in find_prev
Find_prev returned invalid when the contents of the file were equal to
the contents of the search box.

This is due to the checker walking on an empty character at the end of
a line.
2019-08-27 07:17:22 +02:00
Andreas Kling
a6be213287 GTextEditor: Add add_custom_context_menu_action()
This allows embedders to add their own custom GAction set to a text
editor's context menu.
2019-08-25 21:33:08 +02:00
Andreas Kling
fa20dcafb5 GTextEditor: Simplify computation of visual selection start/end
Add Line::visual_line_containing(int column) to easily convert a column
number into a visual line index.
2019-08-25 14:04:46 +02:00
Andreas Kling
5aac652b4b GTextEditor: Relayout when the line-wrapping setting is changed 2019-08-25 12:23:14 +02:00
Andreas Kling
3ca1c72c77 GTextEditor: Unbreak selection painting in the new line-wrapping world
To expand a bit on how the line-wrapping works, each physical line of
text is broken up into multiple visual lines. This is recomputed when
the document changes, or when the widget is resized.

Each GTextEditor::Line keeps track of the visual breaking points, and
also their visual rect in content coordinates. This allows us to do
painting and hit testing reasonably efficiently for now.

This code needs some cleanup, but it's finally in a working state, so
here it goes. :^)
2019-08-25 11:24:23 +02:00
Andreas Kling
7b5bcec562 GTextEditor: Fix computing content x/rect values with line wrapping
This makes the cursor actually get painted on the right visual line
when in line-wrapping mode.
2019-08-25 11:24:23 +02:00
Andreas Kling
2e31b6627e GTextEditor: Take horizontal padding into account for line visual rects 2019-08-25 11:24:23 +02:00
Andreas Kling
9752e683f6 GTextEditor: Start working on a line-wrapping feature
This is not finished, but since the feature is controlled by a runtime
flag, the broken implementation should not affect users of this widget
too much (in theory :^).)
2019-08-25 11:24:23 +02:00
Andreas Kling
23b70d5c59 GTextEditor: Clean up some of the rect computations
Moving some rect computations to separate functions to make it easier
to reuse them.
2019-08-25 07:17:09 +02:00
Andrew Weller
e75e33eb46 TextEditor: Replaced 'Find' button with 'Prev' and 'Next' buttons. 2019-08-24 21:57:42 +02:00
Andreas Kling
748b38d80f GTextEditor: Fix obvious bug in find()
We forgot to rewind the search cursor after a partial match, which
would make us fail to find "xxy" in "xxxxy".
2019-08-23 13:54:25 +02:00