Ver Fonte

LibGUI: Make focus events more aware of why focus is changing

This patch adds GUI::FocusEvent which has a GUI::FocusSource.
The focus source is one of three things:

- Programmatic
- Mouse
- Keyboard

This allows receivers of focus events to implement different behaviors
depending on how they receive/lose focus.
Andreas Kling há 4 anos atrás
pai
commit
75b8f4e4e6

+ 1 - 1
Applications/Browser/ConsoleWidget.cpp

@@ -165,7 +165,7 @@ void ConsoleWidget::clear_output()
     m_output_view->update();
 }
 
-void ConsoleWidget::focusin_event(Core::Event&)
+void ConsoleWidget::focusin_event(GUI::FocusEvent&)
 {
     m_input->set_focus(true);
 }

+ 1 - 1
Applications/Browser/ConsoleWidget.h

@@ -48,7 +48,7 @@ private:
     ConsoleWidget();
 
     virtual bool accepts_focus() const override { return true; }
-    virtual void focusin_event(Core::Event&) override;
+    virtual void focusin_event(GUI::FocusEvent&) override;
 
     RefPtr<GUI::TextBox> m_input;
     RefPtr<Web::PageView> m_output_view;

+ 2 - 2
DevTools/HackStudio/Editor.cpp

@@ -65,7 +65,7 @@ const EditorWrapper& Editor::wrapper() const
     return static_cast<const EditorWrapper&>(*parent());
 }
 
-void Editor::focusin_event(Core::Event& event)
+void Editor::focusin_event(GUI::FocusEvent& event)
 {
     wrapper().set_editor_has_focus({}, true);
     if (on_focus)
@@ -73,7 +73,7 @@ void Editor::focusin_event(Core::Event& event)
     GUI::TextEditor::focusin_event(event);
 }
 
-void Editor::focusout_event(Core::Event& event)
+void Editor::focusout_event(GUI::FocusEvent& event)
 {
     wrapper().set_editor_has_focus({}, false);
     GUI::TextEditor::focusout_event(event);

+ 2 - 2
DevTools/HackStudio/Editor.h

@@ -51,8 +51,8 @@ public:
     BreakpointChangeCallback on_breakpoint_change;
 
 private:
-    virtual void focusin_event(Core::Event&) override;
-    virtual void focusout_event(Core::Event&) override;
+    virtual void focusin_event(GUI::FocusEvent&) override;
+    virtual void focusout_event(GUI::FocusEvent&) override;
     virtual void paint_event(GUI::PaintEvent&) override;
     virtual void mousemove_event(GUI::MouseEvent&) override;
     virtual void mousedown_event(GUI::MouseEvent&) override;

+ 17 - 2
Libraries/LibGUI/Event.h

@@ -30,6 +30,7 @@
 #include <AK/Vector.h>
 #include <Kernel/API/KeyCode.h>
 #include <LibCore/Event.h>
+#include <LibGUI/FocusSource.h>
 #include <LibGUI/WindowType.h>
 #include <LibGfx/Point.h>
 #include <LibGfx/Rect.h>
@@ -76,12 +77,12 @@ public:
         __End_WM_Events,
     };
 
-    Event() {}
+    Event() { }
     explicit Event(Type type)
         : Core::Event(type)
     {
     }
-    virtual ~Event() {}
+    virtual ~Event() { }
 
     bool is_key_event() const { return type() == KeyUp || type() == KeyDown; }
     bool is_paint_event() const { return type() == Paint; }
@@ -387,4 +388,18 @@ public:
     }
 };
 
+class FocusEvent final : public Event {
+public:
+    explicit FocusEvent(Type type, FocusSource source)
+        : Event(type)
+        , m_source(source)
+    {
+    }
+
+    FocusSource source() const { return m_source; }
+
+private:
+    FocusSource m_source { FocusSource::Programmatic };
+};
+
 }

+ 37 - 0
Libraries/LibGUI/FocusSource.h

@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace GUI {
+
+enum class FocusSource {
+    Programmatic,
+    Keyboard,
+    Mouse,
+};
+
+}

+ 2 - 2
Libraries/LibGUI/TextEditor.cpp

@@ -1147,7 +1147,7 @@ void TextEditor::set_cursor(const TextPosition& a_position)
         m_highlighter->cursor_did_change();
 }
 
-void TextEditor::focusin_event(Core::Event&)
+void TextEditor::focusin_event(FocusEvent&)
 {
     m_cursor_state = true;
     update_cursor();
@@ -1156,7 +1156,7 @@ void TextEditor::focusin_event(Core::Event&)
         on_focusin();
 }
 
-void TextEditor::focusout_event(Core::Event&)
+void TextEditor::focusout_event(FocusEvent&)
 {
     stop_timer();
     if (on_focusout)

+ 2 - 2
Libraries/LibGUI/TextEditor.h

@@ -166,8 +166,8 @@ protected:
     virtual void mousemove_event(MouseEvent&) override;
     virtual void doubleclick_event(MouseEvent&) override;
     virtual void keydown_event(KeyEvent&) override;
-    virtual void focusin_event(Core::Event&) override;
-    virtual void focusout_event(Core::Event&) override;
+    virtual void focusin_event(FocusEvent&) override;
+    virtual void focusout_event(FocusEvent&) override;
     virtual void timer_event(Core::TimerEvent&) override;
     virtual bool accepts_focus() const override { return true; }
     virtual void enter_event(Core::Event&) override;

+ 16 - 16
Libraries/LibGUI/Widget.cpp

@@ -183,9 +183,9 @@ void Widget::event(Core::Event& event)
     case Event::Resize:
         return handle_resize_event(static_cast<ResizeEvent&>(event));
     case Event::FocusIn:
-        return focusin_event(event);
+        return focusin_event(static_cast<FocusEvent&>(event));
     case Event::FocusOut:
-        return focusout_event(event);
+        return focusout_event(static_cast<FocusEvent&>(event));
     case Event::Show:
         return show_event(static_cast<ShowEvent&>(event));
     case Event::Hide:
@@ -302,7 +302,7 @@ void Widget::handle_mouseup_event(MouseEvent& event)
 void Widget::handle_mousedown_event(MouseEvent& event)
 {
     if (accepts_focus())
-        set_focus(true);
+        set_focus(true, FocusSource::Mouse);
     mousedown_event(event);
     if (event.button() == MouseButton::Right) {
         ContextMenuEvent c_event(event.position(), screen_relative_rect().location().translated(event.position()));
@@ -356,9 +356,9 @@ void Widget::keydown_event(KeyEvent& event)
 {
     if (!event.alt() && !event.ctrl() && !event.logo() && event.key() == KeyCode::Key_Tab) {
         if (event.shift())
-            focus_previous_widget();
+            focus_previous_widget(FocusSource::Keyboard);
         else
-            focus_next_widget();
+            focus_next_widget(FocusSource::Keyboard);
         event.accept();
         return;
     }
@@ -390,11 +390,11 @@ void Widget::context_menu_event(ContextMenuEvent&)
 {
 }
 
-void Widget::focusin_event(Core::Event&)
+void Widget::focusin_event(FocusEvent&)
 {
 }
 
-void Widget::focusout_event(Core::Event&)
+void Widget::focusout_event(FocusEvent&)
 {
 }
 
@@ -513,16 +513,16 @@ bool Widget::is_focused() const
     return false;
 }
 
-void Widget::set_focus(bool focus)
+void Widget::set_focus(bool focus, FocusSource source)
 {
     auto* win = window();
     if (!win)
         return;
     if (focus) {
-        win->set_focused_widget(this);
+        win->set_focused_widget(this, source);
     } else {
         if (win->focused_widget() == this)
-            win->set_focused_widget(nullptr);
+            win->set_focused_widget(nullptr, source);
     }
 }
 
@@ -703,29 +703,29 @@ void Widget::set_updates_enabled(bool enabled)
         update();
 }
 
-void Widget::focus_previous_widget()
+void Widget::focus_previous_widget(FocusSource source)
 {
     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);
+            focusable_widgets[i - 1]->set_focus(true, source);
         else
-            focusable_widgets.last()->set_focus(true);
+            focusable_widgets.last()->set_focus(true, source);
     }
 }
 
-void Widget::focus_next_widget()
+void Widget::focus_next_widget(FocusSource source)
 {
     auto focusable_widgets = window()->focusable_widgets();
     for (size_t 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);
+            focusable_widgets[i + 1]->set_focus(true, source);
         else
-            focusable_widgets.first()->set_focus(true);
+            focusable_widgets.first()->set_focus(true, source);
     }
 }
 

+ 5 - 5
Libraries/LibGUI/Widget.h

@@ -147,7 +147,7 @@ public:
     virtual bool accepts_focus() const { return false; }
 
     bool is_focused() const;
-    void set_focus(bool);
+    void set_focus(bool, FocusSource = FocusSource::Programmatic);
 
     enum class ShouldRespectGreediness { No = 0,
         Yes };
@@ -287,8 +287,8 @@ protected:
     virtual void mousewheel_event(MouseEvent&);
     virtual void doubleclick_event(MouseEvent&);
     virtual void context_menu_event(ContextMenuEvent&);
-    virtual void focusin_event(Core::Event&);
-    virtual void focusout_event(Core::Event&);
+    virtual void focusin_event(FocusEvent&);
+    virtual void focusout_event(FocusEvent&);
     virtual void enter_event(Core::Event&);
     virtual void leave_event(Core::Event&);
     virtual void child_event(Core::ChildEvent&) override;
@@ -310,8 +310,8 @@ private:
     void handle_mouseup_event(MouseEvent&);
     void handle_enter_event(Core::Event&);
     void handle_leave_event(Core::Event&);
-    void focus_previous_widget();
-    void focus_next_widget();
+    void focus_previous_widget(FocusSource);
+    void focus_next_widget(FocusSource);
 
     Window* m_window { nullptr };
     RefPtr<Layout> m_layout;

+ 3 - 3
Libraries/LibGUI/Window.cpp

@@ -525,17 +525,17 @@ void Window::set_main_widget(Widget* widget)
     update();
 }
 
-void Window::set_focused_widget(Widget* widget)
+void Window::set_focused_widget(Widget* widget, FocusSource source)
 {
     if (m_focused_widget == widget)
         return;
     if (m_focused_widget) {
-        Core::EventLoop::current().post_event(*m_focused_widget, make<Event>(Event::FocusOut));
+        Core::EventLoop::current().post_event(*m_focused_widget, make<FocusEvent>(Event::FocusOut, source));
         m_focused_widget->update();
     }
     m_focused_widget = widget ? widget->make_weak_ptr() : nullptr;
     if (m_focused_widget) {
-        Core::EventLoop::current().post_event(*m_focused_widget, make<Event>(Event::FocusIn));
+        Core::EventLoop::current().post_event(*m_focused_widget, make<FocusEvent>(Event::FocusIn, source));
         m_focused_widget->update();
     }
 }

+ 2 - 1
Libraries/LibGUI/Window.h

@@ -30,6 +30,7 @@
 #include <AK/String.h>
 #include <AK/WeakPtr.h>
 #include <LibCore/Object.h>
+#include <LibGUI/FocusSource.h>
 #include <LibGUI/Forward.h>
 #include <LibGUI/WindowType.h>
 #include <LibGfx/Color.h>
@@ -152,7 +153,7 @@ public:
 
     Widget* focused_widget() { return m_focused_widget; }
     const Widget* focused_widget() const { return m_focused_widget; }
-    void set_focused_widget(Widget*);
+    void set_focused_widget(Widget*, FocusSource = FocusSource::Programmatic);
 
     void update();
     void update(const Gfx::IntRect&);

+ 2 - 2
Libraries/LibVT/TerminalWidget.cpp

@@ -181,13 +181,13 @@ void TerminalWidget::set_logical_focus(bool focus)
     update();
 }
 
-void TerminalWidget::focusin_event(Core::Event& event)
+void TerminalWidget::focusin_event(GUI::FocusEvent& event)
 {
     set_logical_focus(true);
     return GUI::Frame::focusin_event(event);
 }
 
-void TerminalWidget::focusout_event(Core::Event& event)
+void TerminalWidget::focusout_event(GUI::FocusEvent& event)
 {
     set_logical_focus(false);
     return GUI::Frame::focusout_event(event);

+ 2 - 2
Libraries/LibVT/TerminalWidget.h

@@ -102,8 +102,8 @@ private:
     virtual void mousemove_event(GUI::MouseEvent&) override;
     virtual void mousewheel_event(GUI::MouseEvent&) override;
     virtual void doubleclick_event(GUI::MouseEvent&) override;
-    virtual void focusin_event(Core::Event&) override;
-    virtual void focusout_event(Core::Event&) override;
+    virtual void focusin_event(GUI::FocusEvent&) override;
+    virtual void focusout_event(GUI::FocusEvent&) override;
     virtual void context_menu_event(GUI::ContextMenuEvent&) override;
     virtual void drop_event(GUI::DropEvent&) override;
     virtual void leave_event(Core::Event&) override;