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:
parent
110b3d89d3
commit
75b8f4e4e6
Notes:
sideshowbarker
2024-07-19 03:38:10 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/75b8f4e4e6a
14 changed files with 94 additions and 41 deletions
Applications/Browser
DevTools/HackStudio
Libraries
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Libraries/LibGUI/FocusSource.h
Normal file
37
Libraries/LibGUI/FocusSource.h
Normal 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,
|
||||
};
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue