mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-04 01:05:58 +03:00
LibGUI: Support cycling through focusable widgets with Tab and Shift-Tab.
This commit is contained in:
parent
01ffcdfa31
commit
ad731cc08f
Notes:
sideshowbarker
2024-07-19 14:08:48 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/ad731cc08f2
@ -21,7 +21,6 @@ private:
|
|||||||
virtual void paint_event(GPaintEvent&) override;
|
virtual void paint_event(GPaintEvent&) override;
|
||||||
virtual void mousedown_event(GMouseEvent&) override;
|
virtual void mousedown_event(GMouseEvent&) override;
|
||||||
virtual void mousemove_event(GMouseEvent&) override;
|
virtual void mousemove_event(GMouseEvent&) override;
|
||||||
virtual bool accepts_focus() const override { return true; }
|
|
||||||
|
|
||||||
void draw_at_mouse(const GMouseEvent&);
|
void draw_at_mouse(const GMouseEvent&);
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
virtual void paint_event(GPaintEvent&) override;
|
virtual void paint_event(GPaintEvent&) override;
|
||||||
virtual void mousedown_event(GMouseEvent&) override;
|
virtual void mousedown_event(GMouseEvent&) override;
|
||||||
virtual bool accepts_focus() const override { return true; }
|
|
||||||
|
|
||||||
Rect get_outer_rect(byte glyph) const;
|
Rect get_outer_rect(byte glyph) const;
|
||||||
|
|
||||||
|
@ -60,9 +60,15 @@ void GButton::paint_event(GPaintEvent& event)
|
|||||||
content_rect.move_by(m_icon->width() + 4, 0);
|
content_rect.move_by(m_icon->width() + 4, 0);
|
||||||
content_rect.set_width(content_rect.width() - m_icon->width() - 4);
|
content_rect.set_width(content_rect.width() - m_icon->width() - 4);
|
||||||
}
|
}
|
||||||
if (is_enabled())
|
if (is_enabled()) {
|
||||||
painter.draw_text(content_rect, m_caption, font, text_alignment(), foreground_color(), TextElision::Right);
|
painter.draw_text(content_rect, m_caption, font, text_alignment(), foreground_color(), TextElision::Right);
|
||||||
else {
|
if (is_focused()) {
|
||||||
|
Rect focus_rect = { 0, 0, font.width(m_caption), font.glyph_height() };
|
||||||
|
focus_rect.inflate(6, 4);
|
||||||
|
focus_rect.center_within(content_rect);
|
||||||
|
painter.draw_rect(focus_rect, Color(140, 140, 140));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
painter.draw_text(content_rect.translated(1, 1), m_caption, font, text_alignment(), Color::White, TextElision::Right);
|
painter.draw_text(content_rect.translated(1, 1), m_caption, font, text_alignment(), Color::White, TextElision::Right);
|
||||||
painter.draw_text(content_rect, m_caption, font, text_alignment(), Color::from_rgb(0x808080), TextElision::Right);
|
painter.draw_text(content_rect, m_caption, font, text_alignment(), Color::from_rgb(0x808080), TextElision::Right);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ public:
|
|||||||
void set_action(GAction&);
|
void set_action(GAction&);
|
||||||
|
|
||||||
virtual const char* class_name() const override { return "GButton"; }
|
virtual const char* class_name() const override { return "GButton"; }
|
||||||
|
virtual bool accepts_focus() const override { return true; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void paint_event(GPaintEvent&) override;
|
virtual void paint_event(GPaintEvent&) override;
|
||||||
|
@ -60,7 +60,9 @@ void GCheckBox::paint_event(GPaintEvent& event)
|
|||||||
|
|
||||||
auto text_rect = rect();
|
auto text_rect = rect();
|
||||||
text_rect.set_left(s_box_width + 4);
|
text_rect.set_left(s_box_width + 4);
|
||||||
|
text_rect.set_width(font().width(m_caption));
|
||||||
text_rect.set_top(height() / 2 - font().glyph_height() / 2);
|
text_rect.set_top(height() / 2 - font().glyph_height() / 2);
|
||||||
|
text_rect.set_height(font().glyph_height());
|
||||||
|
|
||||||
if (fill_with_background_color())
|
if (fill_with_background_color())
|
||||||
painter.fill_rect(rect(), background_color());
|
painter.fill_rect(rect(), background_color());
|
||||||
@ -78,8 +80,14 @@ void GCheckBox::paint_event(GPaintEvent& event)
|
|||||||
if (m_checked)
|
if (m_checked)
|
||||||
painter.draw_bitmap(box_rect.shrunken(4, 4).location(), *s_checked_bitmap, foreground_color());
|
painter.draw_bitmap(box_rect.shrunken(4, 4).location(), *s_checked_bitmap, foreground_color());
|
||||||
|
|
||||||
if (!caption().is_empty())
|
if (!caption().is_empty()) {
|
||||||
painter.draw_text(text_rect, caption(), TextAlignment::TopLeft, foreground_color());
|
painter.draw_text(text_rect, caption(), TextAlignment::TopLeft, foreground_color());
|
||||||
|
if (is_focused()) {
|
||||||
|
Rect focus_rect = text_rect;
|
||||||
|
focus_rect.inflate(6, 4);
|
||||||
|
painter.draw_rect(focus_rect, Color(140, 140, 140));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCheckBox::mousemove_event(GMouseEvent& event)
|
void GCheckBox::mousemove_event(GMouseEvent& event)
|
||||||
|
@ -315,6 +315,9 @@ void GTextEditor::toggle_selection_if_needed_for_event(const GKeyEvent& event)
|
|||||||
|
|
||||||
void GTextEditor::keydown_event(GKeyEvent& event)
|
void GTextEditor::keydown_event(GKeyEvent& event)
|
||||||
{
|
{
|
||||||
|
if (is_single_line() && event.key() == KeyCode::Key_Tab)
|
||||||
|
return GWidget::keydown_event(event);
|
||||||
|
|
||||||
if (event.key() == KeyCode::Key_Escape) {
|
if (event.key() == KeyCode::Key_Escape) {
|
||||||
if (on_escape_pressed)
|
if (on_escape_pressed)
|
||||||
on_escape_pressed();
|
on_escape_pressed();
|
||||||
@ -505,8 +508,6 @@ void GTextEditor::keydown_event(GKeyEvent& event)
|
|||||||
|
|
||||||
if (!is_readonly() && !event.ctrl() && !event.alt() && !event.text().is_empty())
|
if (!is_readonly() && !event.ctrl() && !event.alt() && !event.text().is_empty())
|
||||||
insert_at_cursor_or_replace_selection(event.text());
|
insert_at_cursor_or_replace_selection(event.text());
|
||||||
|
|
||||||
return GWidget::keydown_event(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GTextEditor::delete_current_line()
|
void GTextEditor::delete_current_line()
|
||||||
|
@ -243,8 +243,14 @@ void GWidget::hide_event(GHideEvent&)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void GWidget::keydown_event(GKeyEvent&)
|
void GWidget::keydown_event(GKeyEvent& event)
|
||||||
{
|
{
|
||||||
|
if (!event.alt() && !event.ctrl() && !event.logo() && event.key() == KeyCode::Key_Tab) {
|
||||||
|
if (event.shift())
|
||||||
|
focus_previous_widget();
|
||||||
|
else
|
||||||
|
focus_next_widget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GWidget::keyup_event(GKeyEvent&)
|
void GWidget::keyup_event(GKeyEvent&)
|
||||||
@ -543,3 +549,29 @@ void GWidget::set_updates_enabled(bool enabled)
|
|||||||
if (enabled)
|
if (enabled)
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GWidget::focus_previous_widget()
|
||||||
|
{
|
||||||
|
auto focusable_widgets = window()->focusable_widgets();
|
||||||
|
for (int i = focusable_widgets.size() - 1; i >= 0; --i) {
|
||||||
|
if (focusable_widgets[i] != this)
|
||||||
|
continue;
|
||||||
|
if (i > 0)
|
||||||
|
focusable_widgets[i - 1]->set_focus(true);
|
||||||
|
else
|
||||||
|
focusable_widgets.last()->set_focus(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GWidget::focus_next_widget()
|
||||||
|
{
|
||||||
|
auto focusable_widgets = window()->focusable_widgets();
|
||||||
|
for (int i = 0; i < focusable_widgets.size(); ++i) {
|
||||||
|
if (focusable_widgets[i] != this)
|
||||||
|
continue;
|
||||||
|
if (i < focusable_widgets.size() - 1)
|
||||||
|
focusable_widgets[i + 1]->set_focus(true);
|
||||||
|
else
|
||||||
|
focusable_widgets.first()->set_focus(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -194,6 +194,8 @@ private:
|
|||||||
void handle_enter_event(CEvent&);
|
void handle_enter_event(CEvent&);
|
||||||
void handle_leave_event(CEvent&);
|
void handle_leave_event(CEvent&);
|
||||||
void do_layout();
|
void do_layout();
|
||||||
|
void focus_previous_widget();
|
||||||
|
void focus_next_widget();
|
||||||
|
|
||||||
CElapsedTimer& click_clock(GMouseButton);
|
CElapsedTimer& click_clock(GMouseButton);
|
||||||
|
|
||||||
|
@ -513,3 +513,27 @@ void GWindow::start_wm_resize()
|
|||||||
message.wm.window_id = m_window_id;
|
message.wm.window_id = m_window_id;
|
||||||
GEventLoop::post_message_to_server(message);
|
GEventLoop::post_message_to_server(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<GWidget*> GWindow::focusable_widgets() const
|
||||||
|
{
|
||||||
|
if (!m_main_widget)
|
||||||
|
return { };
|
||||||
|
|
||||||
|
Vector<GWidget*> collected_widgets;
|
||||||
|
|
||||||
|
Function<void(GWidget&)> collect_focusable_widgets = [&] (GWidget& widget) {
|
||||||
|
if (widget.accepts_focus())
|
||||||
|
collected_widgets.append(&widget);
|
||||||
|
for (auto& child : widget.children()) {
|
||||||
|
if (!child->is_widget())
|
||||||
|
continue;
|
||||||
|
auto& child_widget = *static_cast<GWidget*>(child);
|
||||||
|
if (!child_widget.is_visible())
|
||||||
|
continue;
|
||||||
|
collect_focusable_widgets(child_widget);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
collect_focusable_widgets(*m_main_widget);
|
||||||
|
return collected_widgets;
|
||||||
|
}
|
||||||
|
@ -113,6 +113,8 @@ public:
|
|||||||
String icon_path() const { return m_icon_path; }
|
String icon_path() const { return m_icon_path; }
|
||||||
void set_icon_path(const String&);
|
void set_icon_path(const String&);
|
||||||
|
|
||||||
|
Vector<GWidget*> focusable_widgets() const;
|
||||||
|
|
||||||
virtual const char* class_name() const override { return "GWindow"; }
|
virtual const char* class_name() const override { return "GWindow"; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
Loading…
Reference in New Issue
Block a user