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.
This commit is contained in:
Andreas Kling 2020-08-14 19:56:40 +02:00
parent 110b3d89d3
commit 75b8f4e4e6
Notes: sideshowbarker 2024-07-19 03:38:10 +09:00
14 changed files with 94 additions and 41 deletions

View file

@ -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);
}

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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 };
};
}

View file

@ -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,
};
}

View file

@ -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)

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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();
}
}

View file

@ -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&);

View file

@ -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);

View file

@ -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;