mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:02:45 +00:00 
			
		
		
		
	
						commit
						940eb1bbeb
					
				
					 7 changed files with 131 additions and 5 deletions
				
			
		|  | @ -31,7 +31,7 @@ void GAbstractButton::set_checked(bool checked) | ||||||
|     m_checked = checked; |     m_checked = checked; | ||||||
| 
 | 
 | ||||||
|     if (is_exclusive() && checked) { |     if (is_exclusive() && checked) { | ||||||
|         parent_widget()->for_each_child_of_type<GAbstractButton>([&] (auto& sibling) { |         parent_widget()->for_each_child_of_type<GAbstractButton>([&](auto& sibling) { | ||||||
|             if (!sibling.is_exclusive() || !sibling.is_checkable() || !sibling.is_checked()) |             if (!sibling.is_exclusive() || !sibling.is_checkable() || !sibling.is_checked()) | ||||||
|                 return IterationDecision::Continue; |                 return IterationDecision::Continue; | ||||||
|             sibling.m_checked = false; |             sibling.m_checked = false; | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ public: | ||||||
|     virtual void click() = 0; |     virtual void click() = 0; | ||||||
|     virtual const char* class_name() const override { return "GAbstractButton"; } |     virtual const char* class_name() const override { return "GAbstractButton"; } | ||||||
|     virtual bool accepts_focus() const override { return true; } |     virtual bool accepts_focus() const override { return true; } | ||||||
|  |     virtual bool accepts_keyboard_select() const { return true; } | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     explicit GAbstractButton(GWidget* parent); |     explicit GAbstractButton(GWidget* parent); | ||||||
|  |  | ||||||
|  | @ -66,6 +66,11 @@ void GButton::click() | ||||||
|         on_click(*this); |         on_click(*this); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool GButton::accepts_keyboard_select() const | ||||||
|  | { | ||||||
|  |     return is_enabled(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void GButton::set_action(GAction& action) | void GButton::set_action(GAction& action) | ||||||
| { | { | ||||||
|     m_action = action.make_weak_ptr(); |     m_action = action.make_weak_ptr(); | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     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; } |     virtual bool accepts_focus() const override { return true; } | ||||||
|  |     virtual bool accepts_keyboard_select() const; | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     virtual void paint_event(GPaintEvent&) override; |     virtual void paint_event(GPaintEvent&) override; | ||||||
|  |  | ||||||
|  | @ -102,6 +102,7 @@ public: | ||||||
|     void update(const Rect&); |     void update(const Rect&); | ||||||
| 
 | 
 | ||||||
|     virtual bool accepts_focus() const { return false; } |     virtual bool accepts_focus() const { return false; } | ||||||
|  |     virtual bool accepts_keyboard_select() const { return false; } | ||||||
| 
 | 
 | ||||||
|     bool is_focused() const; |     bool is_focused() const; | ||||||
|     void set_focus(bool); |     void set_focus(bool); | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| #include "GEventLoop.h" | #include "GEventLoop.h" | ||||||
| #include "GWidget.h" | #include "GWidget.h" | ||||||
| #include <AK/HashMap.h> | #include <AK/HashMap.h> | ||||||
|  | #include <AK/StringBuilder.h> | ||||||
| #include <LibC/stdio.h> | #include <LibC/stdio.h> | ||||||
| #include <LibC/stdlib.h> | #include <LibC/stdlib.h> | ||||||
| #include <LibC/unistd.h> | #include <LibC/unistd.h> | ||||||
|  | @ -241,6 +242,8 @@ void GWindow::event(CEvent& event) | ||||||
|         for (auto& rect : rects) |         for (auto& rect : rects) | ||||||
|             m_main_widget->event(*make<GPaintEvent>(rect)); |             m_main_widget->event(*make<GPaintEvent>(rect)); | ||||||
| 
 | 
 | ||||||
|  |         paint_keybinds(); | ||||||
|  | 
 | ||||||
|         if (m_double_buffering_enabled) |         if (m_double_buffering_enabled) | ||||||
|             flip(rects); |             flip(rects); | ||||||
|         else if (created_new_backing_store) |         else if (created_new_backing_store) | ||||||
|  | @ -262,14 +265,53 @@ void GWindow::event(CEvent& event) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (event.type() == GEvent::KeyUp || event.type() == GEvent::KeyDown) { |     if (event.type() == GEvent::KeyUp || event.type() == GEvent::KeyDown) { | ||||||
|  |         auto keyevent = static_cast<GKeyEvent&>(event); | ||||||
|  |         if (keyevent.logo() && event.type() == GEvent::KeyUp) { | ||||||
|  |             if (m_keybind_mode) { | ||||||
|  |                 m_keybind_mode = false; | ||||||
|  |             } else { | ||||||
|  |                 m_keybind_mode = true; | ||||||
|  |                 find_keyboard_selectable(); | ||||||
|  |                 m_entered_keybind = ""; | ||||||
|  |             } | ||||||
|  |             update(); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (m_keybind_mode) { | ||||||
|  |             if (event.type() == GEvent::KeyUp) { | ||||||
|  |                 StringBuilder builder; | ||||||
|  |                 builder.append(m_entered_keybind); | ||||||
|  |                 builder.append(keyevent.text()); | ||||||
|  |                 m_entered_keybind = builder.to_string(); | ||||||
|  | 
 | ||||||
|  |                 auto found_widget = m_hashed_potential_keybind_widgets.find(m_entered_keybind); | ||||||
|  |                 if (found_widget != m_hashed_potential_keybind_widgets.end()) { | ||||||
|  |                     m_keybind_mode = false; | ||||||
|  |                     auto event = make<GMouseEvent>(GEvent::MouseDown, Point(), 0, GMouseButton::Left, 0, 0); | ||||||
|  |                     found_widget->value->event(*event); | ||||||
|  |                     event = make<GMouseEvent>(GEvent::MouseUp, Point(), 0, GMouseButton::Left, 0, 0); | ||||||
|  |                     found_widget->value->event(*event); | ||||||
|  |                 } else if (m_entered_keybind.length() >= m_max_keybind_length) { | ||||||
|  |                     m_keybind_mode = false; | ||||||
|  |                 } | ||||||
|  |                 update(); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|             if (m_focused_widget) |             if (m_focused_widget) | ||||||
|                 return m_focused_widget->event(event); |                 return m_focused_widget->event(event); | ||||||
|             if (m_main_widget) |             if (m_main_widget) | ||||||
|                 return m_main_widget->event(event); |                 return m_main_widget->event(event); | ||||||
|  |         } | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (event.type() == GEvent::WindowBecameActive || event.type() == GEvent::WindowBecameInactive) { |     if (event.type() == GEvent::WindowBecameActive || event.type() == GEvent::WindowBecameInactive) { | ||||||
|  |         if (event.type() == GEvent::WindowBecameInactive && m_keybind_mode) { | ||||||
|  |             m_keybind_mode = false; | ||||||
|  |             update(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         m_is_active = event.type() == GEvent::WindowBecameActive; |         m_is_active = event.type() == GEvent::WindowBecameActive; | ||||||
|         if (m_main_widget) |         if (m_main_widget) | ||||||
|             m_main_widget->event(event); |             m_main_widget->event(event); | ||||||
|  | @ -307,6 +349,72 @@ void GWindow::event(CEvent& event) | ||||||
|     CObject::event(event); |     CObject::event(event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void GWindow::paint_keybinds() | ||||||
|  | { | ||||||
|  |     if (!m_keybind_mode) | ||||||
|  |         return; | ||||||
|  |     GPainter painter(*m_main_widget); | ||||||
|  | 
 | ||||||
|  |     for (auto& keypair : m_hashed_potential_keybind_widgets) { | ||||||
|  |         auto widget = keypair.value; | ||||||
|  |         bool could_be_keybind = true; | ||||||
|  |         for (size_t i = 0; i < m_entered_keybind.length(); i++) { | ||||||
|  |             if (keypair.key.characters()[i] != m_entered_keybind.characters()[i]) { | ||||||
|  |                 could_be_keybind = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (could_be_keybind) { | ||||||
|  |             auto rect = Rect(widget->x() - 5, widget->y() - 5, 4 + Font::default_font().width(keypair.key), 16); | ||||||
|  |             auto highlight_rect = Rect(widget->x() - 3, widget->y() - 5, 0, 16); | ||||||
|  | 
 | ||||||
|  |             painter.fill_rect(rect, Color::LightGray); | ||||||
|  |             painter.draw_rect(rect, Color::Black, false); | ||||||
|  |             painter.draw_text(rect, keypair.key.characters(), TextAlignment::Center, Color::Black); | ||||||
|  |             painter.draw_text(highlight_rect, m_entered_keybind.characters(), TextAlignment::CenterLeft, Color::MidGray); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GWindow::find_keyboard_selectable() | ||||||
|  | { | ||||||
|  |     Vector<GWidget*> potential_keybind_widgets; | ||||||
|  |     m_hashed_potential_keybind_widgets.clear(); | ||||||
|  |     find_keyboard_selectable_children(m_main_widget, potential_keybind_widgets); | ||||||
|  | 
 | ||||||
|  |     m_max_keybind_length = ceil_div(potential_keybind_widgets.size(), ('z' - 'a')); | ||||||
|  |     size_t buffer_length = m_max_keybind_length + 1; | ||||||
|  |     char keybind_buffer[buffer_length]; | ||||||
|  |     for (size_t i = 0; i < buffer_length - 1; i++) { | ||||||
|  |         keybind_buffer[i] = 'a'; | ||||||
|  |     } | ||||||
|  |     keybind_buffer[buffer_length - 1] = '\0'; | ||||||
|  | 
 | ||||||
|  |     for (auto& widget : potential_keybind_widgets) { | ||||||
|  |         m_hashed_potential_keybind_widgets.set(String(keybind_buffer), widget); | ||||||
|  | 
 | ||||||
|  |         for (size_t i = 0; i < buffer_length - 1; i++) { | ||||||
|  |             if (keybind_buffer[i] >= 'z') { | ||||||
|  |                 keybind_buffer[i] = 'a'; | ||||||
|  |             } else { | ||||||
|  |                 keybind_buffer[i]++; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GWindow::find_keyboard_selectable_children(GWidget* widget, Vector<GWidget*>& potential_keybind_widgets) | ||||||
|  | { | ||||||
|  |     widget->for_each_child_widget([&](auto& child) { | ||||||
|  |         if (child.accepts_keyboard_select()) { | ||||||
|  |             potential_keybind_widgets.append(&child); | ||||||
|  |             find_keyboard_selectable_children(&child, potential_keybind_widgets); | ||||||
|  |         } | ||||||
|  |         return IterationDecision::Continue; | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool GWindow::is_visible() const | bool GWindow::is_visible() const | ||||||
| { | { | ||||||
|     return false; |     return false; | ||||||
|  | @ -316,6 +424,7 @@ void GWindow::update(const Rect& a_rect) | ||||||
| { | { | ||||||
|     if (!m_window_id) |     if (!m_window_id) | ||||||
|         return; |         return; | ||||||
|  | 
 | ||||||
|     for (auto& pending_rect : m_pending_paint_event_rects) { |     for (auto& pending_rect : m_pending_paint_event_rects) { | ||||||
|         if (pending_rect.contains(a_rect)) { |         if (pending_rect.contains(a_rect)) { | ||||||
| #ifdef UPDATE_COALESCING_DEBUG | #ifdef UPDATE_COALESCING_DEBUG | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <AK/AKString.h> | #include <AK/AKString.h> | ||||||
|  | #include <AK/HashMap.h> | ||||||
| #include <AK/WeakPtr.h> | #include <AK/WeakPtr.h> | ||||||
| #include <LibCore/CObject.h> | #include <LibCore/CObject.h> | ||||||
| #include <LibGUI/GWindowType.h> | #include <LibGUI/GWindowType.h> | ||||||
|  | @ -130,6 +131,10 @@ protected: | ||||||
| private: | private: | ||||||
|     virtual bool is_window() const override final { return true; } |     virtual bool is_window() const override final { return true; } | ||||||
| 
 | 
 | ||||||
|  |     void paint_keybinds(); | ||||||
|  | 
 | ||||||
|  |     void find_keyboard_selectable(); | ||||||
|  |     void find_keyboard_selectable_children(GWidget* widget, Vector<GWidget*>& potential_keybind_widgets); | ||||||
|     Retained<GraphicsBitmap> create_backing_bitmap(const Size&); |     Retained<GraphicsBitmap> create_backing_bitmap(const Size&); | ||||||
|     void set_current_backing_bitmap(GraphicsBitmap&, bool flush_immediately = false); |     void set_current_backing_bitmap(GraphicsBitmap&, bool flush_immediately = false); | ||||||
|     void flip(const Vector<Rect, 32>& dirty_rects); |     void flip(const Vector<Rect, 32>& dirty_rects); | ||||||
|  | @ -159,4 +164,8 @@ private: | ||||||
|     bool m_resizable { true }; |     bool m_resizable { true }; | ||||||
|     bool m_fullscreen { false }; |     bool m_fullscreen { false }; | ||||||
|     bool m_show_titlebar { true }; |     bool m_show_titlebar { true }; | ||||||
|  |     bool m_keybind_mode { false }; | ||||||
|  |     String m_entered_keybind; | ||||||
|  |     size_t m_max_keybind_length { 0 }; | ||||||
|  |     HashMap<String, GWidget*> m_hashed_potential_keybind_widgets; | ||||||
| }; | }; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling