From dea399ff08320562cd4abe9df25c7c97d4a4215a Mon Sep 17 00:00:00 2001 From: Zac Date: Thu, 10 Dec 2020 14:18:29 +1000 Subject: [PATCH] FileManager: focus_dependent_delete_action is correctly enabled/disabled The focus_dependent_delete_action that sits in the file manager's toolbar would always remain enabled, even if nothing was selected, activating it if nothing was selected would then crash the application. The action is now correctly enabled/disabled, but due to the way selection works in TreeViews, something is always selected, what really matters is if the TreeView has something selected, and it has focus. As it currently stands, there is no way to know when the TreeView's is_focused status changes. In order for this to work I added a callback to the Widget class which fires when a widget receives or looses focus. In that callback, the focus_dependent_delete_action's enabled value is recalculated. --- Applications/FileManager/main.cpp | 12 +++++++++++- Libraries/LibGUI/Widget.h | 2 ++ Libraries/LibGUI/Window.cpp | 15 +++++++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Applications/FileManager/main.cpp b/Applications/FileManager/main.cpp index f01cefd55d..d8b0991620 100644 --- a/Applications/FileManager/main.cpp +++ b/Applications/FileManager/main.cpp @@ -456,6 +456,7 @@ int run_in_windowed_mode(RefPtr config, String initial_locatio directory_view.delete_action().activate(); refresh_tree_view(); }); + focus_dependent_delete_action->set_enabled(false); auto mkdir_action = GUI::Action::create("New directory...", { Mod_Ctrl | Mod_Shift, Key_N }, Gfx::Bitmap::load_from_file("/res/icons/16x16/mkdir.png"), [&](const GUI::Action&) { directory_view.mkdir_action().activate(); @@ -579,9 +580,10 @@ int run_in_windowed_mode(RefPtr config, String initial_locatio }; directory_view.on_selection_change = [&](GUI::AbstractView& view) { - // FIXME: Figure out how we can enable/disable the paste action, based on clipboard contents. auto& selection = view.selection(); copy_action->set_enabled(!selection.is_empty()); + focus_dependent_delete_action->set_enabled((!tree_view.selection().is_empty() && tree_view.is_focused()) + || !directory_view.current_view().selection().is_empty()); }; directory_context_menu->add_action(copy_action); @@ -681,6 +683,9 @@ int run_in_windowed_mode(RefPtr config, String initial_locatio }; tree_view.on_selection_change = [&] { + focus_dependent_delete_action->set_enabled((!tree_view.selection().is_empty() && tree_view.is_focused()) + || !directory_view.current_view().selection().is_empty()); + if (tree_view.selection().is_empty()) return; auto path = directories_model->full_path(tree_view.selection().first()); @@ -692,6 +697,11 @@ int run_in_windowed_mode(RefPtr config, String initial_locatio directory_view.delete_action().set_enabled(!tree_view.selection().is_empty()); }; + tree_view.on_focus_change = [&]([[ maybe_unused ]] const bool has_focus, [[ maybe_unused ]] const GUI::FocusSource source) { + focus_dependent_delete_action->set_enabled((!tree_view.selection().is_empty() && has_focus) + || !directory_view.current_view().selection().is_empty()); + }; + tree_view.on_context_menu_request = [&](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) { if (index.is_valid()) { tree_view_directory_context_menu->popup(event.screen_position()); diff --git a/Libraries/LibGUI/Widget.h b/Libraries/LibGUI/Widget.h index 10d3366cfc..ec8e5b5f7c 100644 --- a/Libraries/LibGUI/Widget.h +++ b/Libraries/LibGUI/Widget.h @@ -165,6 +165,8 @@ public: bool is_focused() const; void set_focus(bool, FocusSource = FocusSource::Programmatic); + Function on_focus_change; + // Returns true if this widget or one of its descendants is focused. bool has_focus_within() const; diff --git a/Libraries/LibGUI/Window.cpp b/Libraries/LibGUI/Window.cpp index 6faaa5a08a..7389b1d350 100644 --- a/Libraries/LibGUI/Window.cpp +++ b/Libraries/LibGUI/Window.cpp @@ -558,14 +558,21 @@ 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::FocusOut, source)); - m_focused_widget->update(); - } + + WeakPtr previously_focused_widget = m_focused_widget; m_focused_widget = widget; + + if (previously_focused_widget) { + Core::EventLoop::current().post_event(*previously_focused_widget, make(Event::FocusOut, source)); + previously_focused_widget->update(); + if (previously_focused_widget && previously_focused_widget->on_focus_change) + previously_focused_widget->on_focus_change(previously_focused_widget->is_focused(), source); + } if (m_focused_widget) { Core::EventLoop::current().post_event(*m_focused_widget, make(Event::FocusIn, source)); m_focused_widget->update(); + if (m_focused_widget && m_focused_widget->on_focus_change) + m_focused_widget->on_focus_change(m_focused_widget->is_focused(), source); } }