mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 15:28:11 +00:00
GTextEditor: Add support for different text alignments.
Currently only CenterLeft and CenterRight are supported, but it would be very straightforward to add other alignments as well.
This commit is contained in:
parent
16f6a3af3c
commit
62f0aae4ca
2 changed files with 57 additions and 9 deletions
|
@ -107,8 +107,22 @@ GTextPosition GTextEditor::text_position_at(const Point& a_position) const
|
||||||
position.move_by(horizontal_scrollbar().value(), vertical_scrollbar().value());
|
position.move_by(horizontal_scrollbar().value(), vertical_scrollbar().value());
|
||||||
position.move_by(-(m_horizontal_content_padding + ruler_width()), 0);
|
position.move_by(-(m_horizontal_content_padding + ruler_width()), 0);
|
||||||
int line_index = position.y() / line_height();
|
int line_index = position.y() / line_height();
|
||||||
int column_index = position.x() / glyph_width();
|
|
||||||
line_index = max(0, min(line_index, line_count() - 1));
|
line_index = max(0, min(line_index, line_count() - 1));
|
||||||
|
auto& line = *m_lines[line_index];
|
||||||
|
|
||||||
|
int column_index;
|
||||||
|
switch (m_text_alignment) {
|
||||||
|
case TextAlignment::CenterLeft:
|
||||||
|
column_index = position.x() / glyph_width();
|
||||||
|
break;
|
||||||
|
case TextAlignment::CenterRight:
|
||||||
|
column_index = (position.x() - (frame_inner_rect().right() + 1 - (line.length() * glyph_width()))) / glyph_width();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
column_index = max(0, min(column_index, m_lines[line_index]->length()));
|
column_index = max(0, min(column_index, m_lines[line_index]->length()));
|
||||||
return { line_index, column_index };
|
return { line_index, column_index };
|
||||||
}
|
}
|
||||||
|
@ -202,7 +216,7 @@ void GTextEditor::paint_event(GPaintEvent& event)
|
||||||
painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
|
painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
|
||||||
if (m_ruler_visible)
|
if (m_ruler_visible)
|
||||||
painter.translate(ruler_width(), 0);
|
painter.translate(ruler_width(), 0);
|
||||||
int exposed_width = max(content_width(), width());
|
int exposed_width = max(content_width(), frame_inner_rect().width());
|
||||||
|
|
||||||
int first_visible_line = text_position_at(event.rect().top_left()).line();
|
int first_visible_line = text_position_at(event.rect().top_left()).line();
|
||||||
int last_visible_line = text_position_at(event.rect().bottom_right()).line();
|
int last_visible_line = text_position_at(event.rect().bottom_right()).line();
|
||||||
|
@ -232,13 +246,15 @@ void GTextEditor::paint_event(GPaintEvent& event)
|
||||||
line_rect.set_width(exposed_width);
|
line_rect.set_width(exposed_width);
|
||||||
if (is_multi_line() && i == m_cursor.line())
|
if (is_multi_line() && i == m_cursor.line())
|
||||||
painter.fill_rect(line_rect, Color(230, 230, 230));
|
painter.fill_rect(line_rect, Color(230, 230, 230));
|
||||||
painter.draw_text(line_rect, line.characters(), line.length(), TextAlignment::CenterLeft, Color::Black);
|
painter.draw_text(line_rect, line.characters(), line.length(), m_text_alignment, Color::Black);
|
||||||
bool line_has_selection = has_selection && i >= selection.start().line() && i <= selection.end().line();
|
bool line_has_selection = has_selection && i >= selection.start().line() && i <= selection.end().line();
|
||||||
if (line_has_selection) {
|
if (line_has_selection) {
|
||||||
int selection_start_column_on_line = selection.start().line() == i ? selection.start().column() : 0;
|
int selection_start_column_on_line = selection.start().line() == i ? selection.start().column() : 0;
|
||||||
int selection_end_column_on_line = selection.end().line() == i ? selection.end().column() : line.length();
|
int selection_end_column_on_line = selection.end().line() == i ? selection.end().column() : line.length();
|
||||||
int selection_left = m_horizontal_content_padding + selection_start_column_on_line * font().glyph_width('x');
|
|
||||||
int selection_right = line_rect.left() + selection_end_column_on_line * font().glyph_width('x');
|
int selection_left = content_x_for_position({ i, selection_start_column_on_line });
|
||||||
|
int selection_right = content_x_for_position({ i, selection_end_column_on_line });;
|
||||||
|
|
||||||
Rect selection_rect { selection_left, line_rect.y(), selection_right - selection_left, line_rect.height() };
|
Rect selection_rect { selection_left, line_rect.y(), selection_right - selection_left, line_rect.height() };
|
||||||
painter.fill_rect(selection_rect, Color::from_rgb(0x955233));
|
painter.fill_rect(selection_rect, Color::from_rgb(0x955233));
|
||||||
painter.draw_text(selection_rect, line.characters() + selection_start_column_on_line, line.length() - selection_start_column_on_line - (line.length() - selection_end_column_on_line), TextAlignment::CenterLeft, Color::White);
|
painter.draw_text(selection_rect, line.characters() + selection_start_column_on_line, line.length() - selection_start_column_on_line - (line.length() - selection_end_column_on_line), TextAlignment::CenterLeft, Color::White);
|
||||||
|
@ -544,20 +560,38 @@ void GTextEditor::insert_at_cursor(char ch)
|
||||||
did_change();
|
did_change();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GTextEditor::content_x_for_position(const GTextPosition& position) const
|
||||||
|
{
|
||||||
|
auto& line = *m_lines[position.line()];
|
||||||
|
switch (m_text_alignment) {
|
||||||
|
case TextAlignment::CenterLeft:
|
||||||
|
return m_horizontal_content_padding + position.column() * glyph_width();
|
||||||
|
break;
|
||||||
|
case TextAlignment::CenterRight:
|
||||||
|
return frame_inner_rect().right() + 1 - m_horizontal_content_padding - (line.length() * glyph_width()) + (position.column() * glyph_width());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rect GTextEditor::cursor_content_rect() const
|
Rect GTextEditor::cursor_content_rect() const
|
||||||
{
|
{
|
||||||
if (!m_cursor.is_valid())
|
if (!m_cursor.is_valid())
|
||||||
return { };
|
return { };
|
||||||
ASSERT(!m_lines.is_empty());
|
ASSERT(!m_lines.is_empty());
|
||||||
ASSERT(m_cursor.column() <= (current_line().length() + 1));
|
ASSERT(m_cursor.column() <= (current_line().length() + 1));
|
||||||
|
|
||||||
|
int cursor_x = content_x_for_position(m_cursor);
|
||||||
|
|
||||||
if (is_single_line()) {
|
if (is_single_line()) {
|
||||||
Rect cursor_rect = { m_horizontal_content_padding + m_cursor.column() * glyph_width(), 0, 1, font().glyph_height() + 2 };
|
Rect cursor_rect { cursor_x, 0, 1, font().glyph_height() + 2 };
|
||||||
cursor_rect.center_vertically_within(rect());
|
cursor_rect.center_vertically_within(rect());
|
||||||
// FIXME: This would not be necessary if we knew more about the font and could center it based on baseline and x-height.
|
// FIXME: This would not be necessary if we knew more about the font and could center it based on baseline and x-height.
|
||||||
cursor_rect.move_by(0, 1);
|
cursor_rect.move_by(0, 1);
|
||||||
return cursor_rect;
|
return cursor_rect;
|
||||||
}
|
}
|
||||||
return { m_horizontal_content_padding + m_cursor.column() * glyph_width(), m_cursor.line() * line_height(), 1, line_height() };
|
return { cursor_x, m_cursor.line() * line_height(), 1, line_height() };
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect GTextEditor::line_widget_rect(int line_index) const
|
Rect GTextEditor::line_widget_rect(int line_index) const
|
||||||
|
@ -584,7 +618,7 @@ void GTextEditor::scroll_cursor_into_view()
|
||||||
Rect GTextEditor::line_content_rect(int line_index) const
|
Rect GTextEditor::line_content_rect(int line_index) const
|
||||||
{
|
{
|
||||||
if (is_single_line()) {
|
if (is_single_line()) {
|
||||||
Rect line_rect = { m_horizontal_content_padding, 0, content_width(), font().glyph_height() + 2 };
|
Rect line_rect = { m_horizontal_content_padding, 0, content_width() - m_horizontal_content_padding * 2, font().glyph_height() + 2 };
|
||||||
line_rect.center_vertically_within(rect());
|
line_rect.center_vertically_within(rect());
|
||||||
// FIXME: This would not be necessary if we knew more about the font and could center it based on baseline and x-height.
|
// FIXME: This would not be necessary if we knew more about the font and could center it based on baseline and x-height.
|
||||||
line_rect.move_by(0, 1);
|
line_rect.move_by(0, 1);
|
||||||
|
@ -593,7 +627,7 @@ Rect GTextEditor::line_content_rect(int line_index) const
|
||||||
return {
|
return {
|
||||||
m_horizontal_content_padding,
|
m_horizontal_content_padding,
|
||||||
line_index * line_height(),
|
line_index * line_height(),
|
||||||
content_width(),
|
content_width() - m_horizontal_content_padding * 2,
|
||||||
line_height()
|
line_height()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -915,3 +949,11 @@ void GTextEditor::context_menu_event(GContextMenuEvent& event)
|
||||||
}
|
}
|
||||||
m_context_menu->popup(event.screen_position());
|
m_context_menu->popup(event.screen_position());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GTextEditor::set_text_alignment(TextAlignment alignment)
|
||||||
|
{
|
||||||
|
if (m_text_alignment == alignment)
|
||||||
|
return;
|
||||||
|
m_text_alignment = alignment;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <LibGUI/GScrollableWidget.h>
|
#include <LibGUI/GScrollableWidget.h>
|
||||||
#include <AK/Function.h>
|
#include <AK/Function.h>
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
|
#include <SharedGraphics/TextAlignment.h>
|
||||||
|
|
||||||
class GAction;
|
class GAction;
|
||||||
class GMenu;
|
class GMenu;
|
||||||
|
@ -69,6 +70,9 @@ public:
|
||||||
GTextEditor(Type, GWidget* parent);
|
GTextEditor(Type, GWidget* parent);
|
||||||
virtual ~GTextEditor() override;
|
virtual ~GTextEditor() override;
|
||||||
|
|
||||||
|
TextAlignment text_alignment() const { return m_text_alignment; }
|
||||||
|
void set_text_alignment(TextAlignment);
|
||||||
|
|
||||||
Type type() const { return m_type; }
|
Type type() const { return m_type; }
|
||||||
bool is_single_line() const { return m_type == SingleLine; }
|
bool is_single_line() const { return m_type == SingleLine; }
|
||||||
bool is_multi_line() const { return m_type == MultiLine; }
|
bool is_multi_line() const { return m_type == MultiLine; }
|
||||||
|
@ -173,11 +177,13 @@ private:
|
||||||
void insert_at_cursor_or_replace_selection(const String&);
|
void insert_at_cursor_or_replace_selection(const String&);
|
||||||
void delete_selection();
|
void delete_selection();
|
||||||
void did_update_selection();
|
void did_update_selection();
|
||||||
|
int content_x_for_position(const GTextPosition&) const;
|
||||||
|
|
||||||
Type m_type { MultiLine };
|
Type m_type { MultiLine };
|
||||||
|
|
||||||
Vector<OwnPtr<Line>> m_lines;
|
Vector<OwnPtr<Line>> m_lines;
|
||||||
GTextPosition m_cursor;
|
GTextPosition m_cursor;
|
||||||
|
TextAlignment m_text_alignment { TextAlignment::CenterLeft };
|
||||||
bool m_cursor_state { true };
|
bool m_cursor_state { true };
|
||||||
bool m_in_drag_select { false };
|
bool m_in_drag_select { false };
|
||||||
bool m_ruler_visible { true };
|
bool m_ruler_visible { true };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue