From 13b98d01c0cc2a40ab581ad14ab82aa4d6652554 Mon Sep 17 00:00:00 2001 From: david072 Date: Sun, 5 Nov 2023 12:13:19 +0100 Subject: [PATCH] Taskbar/QuickLaunchWidget: Properly save Quick Launch Entries The entries in the QuickLaunchWidget are now saved properly. This means that the format with which they are saved needed to be changed, since we now also need to store the order of the entries. To do this, the entries are now saved using the following value format: ":". When loading, we simply parse this structure out and sort by the index, before parsing the path into `QuickLaunchEntry`s. --- Base/home/anon/.config/Taskbar.ini | 10 +- .../Services/Taskbar/QuickLaunchWidget.cpp | 128 +++++++++++------- Userland/Services/Taskbar/QuickLaunchWidget.h | 26 +++- 3 files changed, 102 insertions(+), 62 deletions(-) diff --git a/Base/home/anon/.config/Taskbar.ini b/Base/home/anon/.config/Taskbar.ini index 1c4a3e5fa56..1f7e43a5508 100644 --- a/Base/home/anon/.config/Taskbar.ini +++ b/Base/home/anon/.config/Taskbar.ini @@ -1,8 +1,8 @@ [Clock] TimeFormat=%T -[QuickLaunch] -Browser=Browser.af -Terminal=Terminal.af -FileManager=FileManager.af -TextEditor=TextEditor.af +[QuickLaunch_Entries] +Browser=0:Browser.af +FileManager=1:FileManager.af +Terminal=2:Terminal.af +TextEditor=3:TextEditor.af diff --git a/Userland/Services/Taskbar/QuickLaunchWidget.cpp b/Userland/Services/Taskbar/QuickLaunchWidget.cpp index 22d77eb20a9..6f09b67132e 100644 --- a/Userland/Services/Taskbar/QuickLaunchWidget.cpp +++ b/Userland/Services/Taskbar/QuickLaunchWidget.cpp @@ -26,8 +26,15 @@ namespace Taskbar { -constexpr auto quick_launch = "QuickLaunch"sv; -constexpr int quick_launch_button_size = 24; +static DeprecatedString sanitize_name(DeprecatedString const& name) +{ + return name.replace(" "sv, ""sv).replace("="sv, ""sv); +} + +static DeprecatedString entry_to_config_string(size_t index, NonnullOwnPtr const& entry) +{ + return DeprecatedString::formatted("{}:{}", index, entry->path()); +} ErrorOr QuickLaunchEntryAppFile::launch() const { @@ -88,20 +95,9 @@ DeprecatedString QuickLaunchEntryFile::name() const ErrorOr> QuickLaunchWidget::create() { - Vector> entries; - auto keys = Config::list_keys("Taskbar"sv, quick_launch); - for (auto& name : keys) { - auto value = Config::read_string("Taskbar"sv, quick_launch, name); - auto entry = QuickLaunchEntry::create_from_config_value(value); - if (!entry) - continue; - - entries.append(entry.release_nonnull()); - } - auto widget = TRY(AK::adopt_nonnull_ref_or_enomem(new (nothrow) QuickLaunchWidget())); TRY(widget->create_context_menu()); - widget->add_quick_launch_buttons(move(entries)); + widget->load_entries(); return widget; } @@ -113,12 +109,44 @@ QuickLaunchWidget::QuickLaunchWidget() set_fixed_height(24); } +void QuickLaunchWidget::load_entries(bool save) +{ + struct ConfigEntry { + int index; + DeprecatedString path; + }; + + Vector config_entries; + auto keys = Config::list_keys(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES); + for (auto& name : keys) { + dbgln("loading key: {}", name); + auto value = Config::read_string(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, name); + auto values = value.split(':'); + + config_entries.append({ values[0].to_int().release_value(), values[1] }); + } + + quick_sort(config_entries, [](ConfigEntry const& a, ConfigEntry const& b) { + return a.index < b.index; + }); + + Vector> entries; + for (auto const& config_entry : config_entries) { + auto entry = QuickLaunchEntry::create_from_config_value(config_entry.path); + if (!entry) + continue; + + entries.append(entry.release_nonnull()); + } + + add_quick_launch_buttons(move(entries), save); +} + ErrorOr QuickLaunchWidget::create_context_menu() { auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/delete.png"sv)); m_context_menu = GUI::Menu::construct(); m_context_menu_default_action = GUI::Action::create("&Remove", icon, [this](auto&) { - Config::remove_key("Taskbar"sv, quick_launch, m_context_menu_app_name); remove_entry(m_context_menu_app_name); resize(); update(); @@ -128,11 +156,14 @@ ErrorOr QuickLaunchWidget::create_context_menu() return {}; } -void QuickLaunchWidget::add_quick_launch_buttons(Vector> entries) +void QuickLaunchWidget::add_quick_launch_buttons(Vector> entries, bool save) { size_t size = entries.size(); - for (size_t i = 0; i < size; i++) + for (size_t i = 0; i < size; i++) { m_entries.append(entries.take(0)); + if (save) + Config::write_string(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, sanitize_name(m_entries.last()->name()), entry_to_config_string(m_entries.size() - 1, m_entries.last())); + } resize(); update(); @@ -163,21 +194,15 @@ OwnPtr QuickLaunchEntry::create_from_path(StringView path) return make(path); } -static DeprecatedString sanitize_entry_name(DeprecatedString const& name) -{ - return name.replace(" "sv, ""sv, ReplaceMode::All).replace("="sv, ""sv, ReplaceMode::All); -} - -ErrorOr QuickLaunchWidget::add_or_adjust_button(DeprecatedString const& button_name, NonnullOwnPtr entry) +ErrorOr QuickLaunchWidget::add_or_adjust_button(DeprecatedString const& button_name, NonnullOwnPtr entry, bool save) { auto file_name_to_watch = entry->file_name_to_watch(); if (!file_name_to_watch.is_empty()) { if (!m_watcher) { m_watcher = TRY(Core::FileWatcher::create()); - m_watcher->on_change = [button_name, this](Core::FileWatcherEvent const& event) { - auto name = sanitize_entry_name(event.event_path); - dbgln("Removing QuickLaunch entry {}", name); - remove_entry(button_name); + m_watcher->on_change = [button_name, save, this](Core::FileWatcherEvent const&) { + dbgln("Removing QuickLaunch entry \"{}\"", button_name); + remove_entry(button_name, save); resize(); update(); }; @@ -185,7 +210,7 @@ ErrorOr QuickLaunchWidget::add_or_adjust_button(DeprecatedString const& bu TRY(m_watcher->add_watch(file_name_to_watch, Core::FileWatcherEvent::Type::Deleted)); } - set_or_insert_entry(move(entry)); + set_or_insert_entry(move(entry), save); resize(); update(); @@ -194,20 +219,14 @@ ErrorOr QuickLaunchWidget::add_or_adjust_button(DeprecatedString const& bu void QuickLaunchWidget::config_key_was_removed(StringView domain, StringView group, StringView key) { - if (domain == "Taskbar" && group == quick_launch) - remove_entry(key); + if (domain == "Taskbar" && group == CONFIG_GROUP_ENTRIES) + remove_entry(key, false); } -void QuickLaunchWidget::config_string_did_change(StringView domain, StringView group, StringView key, StringView value) +void QuickLaunchWidget::config_string_did_change(StringView domain, StringView group, StringView, StringView) { - if (domain == "Taskbar" && group == quick_launch) { - auto entry = QuickLaunchEntry::create_from_config_value(value); - if (!entry) - return; - auto result = add_or_adjust_button(key, entry.release_nonnull()); - if (result.is_error()) - GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Failed to change quick launch entry: {}", result.release_error())); - } + if (domain == "Taskbar" && group == CONFIG_GROUP_ENTRIES) + load_entries(false); } void QuickLaunchWidget::drag_enter_event(GUI::DragEvent& event) @@ -227,11 +246,9 @@ void QuickLaunchWidget::drop_event(GUI::DropEvent& event) auto path = url.serialize_path(); auto entry = QuickLaunchEntry::create_from_path(path); if (entry) { - auto item_name = sanitize_entry_name(entry->name()); - auto result = add_or_adjust_button(item_name, entry.release_nonnull()); + auto result = add_or_adjust_button(entry->name(), entry.release_nonnull()); if (result.is_error()) GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Failed to add quick launch entry: {}", result.release_error())); - Config::write_string("Taskbar"sv, quick_launch, item_name, path); } } } @@ -352,10 +369,10 @@ void QuickLaunchWidget::paint_event(GUI::PaintEvent& event) template void QuickLaunchWidget::for_each_entry(Callback callback) { - Gfx::IntRect rect(0, 0, quick_launch_button_size, quick_launch_button_size); + Gfx::IntRect rect(0, 0, BUTTON_SIZE, BUTTON_SIZE); for (auto const& entry : m_entries) { callback(entry, rect); - rect.translate_by(quick_launch_button_size, 0); + rect.translate_by(BUTTON_SIZE, 0); } } @@ -397,32 +414,38 @@ ErrorOr QuickLaunchWidget::add_from_pid(pid_t pid_to_add) void QuickLaunchWidget::resize() { - set_fixed_width(m_entries.size() * quick_launch_button_size); + set_fixed_width(m_entries.size() * BUTTON_SIZE); } -void QuickLaunchWidget::set_or_insert_entry(NonnullOwnPtr entry) +void QuickLaunchWidget::set_or_insert_entry(NonnullOwnPtr entry, bool save) { auto name = entry->name(); - for (auto& value : m_entries) { + for (size_t i = 0; i < m_entries.size(); i++) { + auto& value = m_entries[i]; if (value->name() != name) continue; value = move(entry); + if (save) + Config::write_string(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, sanitize_name(value->name()), entry_to_config_string(i, value)); return; } + if (save) + Config::write_string(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, sanitize_name(entry->name()), entry_to_config_string(m_entries.size(), entry)); m_entries.append(move(entry)); } -void QuickLaunchWidget::remove_entry(DeprecatedString const& name) +void QuickLaunchWidget::remove_entry(DeprecatedString const& name, bool save) { for (size_t i = 0; i < m_entries.size(); i++) { if (m_entries[i]->name() != name) continue; + if (save) + Config::remove_key(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, m_entries[i]->name()); m_entries.remove(i); return; } } - void QuickLaunchWidget::recalculate_order() { if (!m_dragging) @@ -435,7 +458,7 @@ void QuickLaunchWidget::recalculate_order() } size_t new_index = m_entries.size() + 1; - Gfx::IntRect rect(0, 0, quick_launch_button_size, quick_launch_button_size); + Gfx::IntRect rect(0, 0, BUTTON_SIZE, BUTTON_SIZE); for (size_t i = 0; i < m_entries.size(); i++) { auto left_break_point = i == 0 ? rect.x() + rect.width() / 2 : rect.x(); if (m_mouse_pos.x() < left_break_point) { @@ -448,7 +471,7 @@ void QuickLaunchWidget::recalculate_order() break; } - rect.translate_by(quick_launch_button_size, 0); + rect.translate_by(BUTTON_SIZE, 0); } if (new_index >= m_entries.size() + 1 || new_index == dragged_index) @@ -459,6 +482,9 @@ void QuickLaunchWidget::recalculate_order() auto entry = m_entries.take(dragged_index); m_entries.insert(new_index, move(entry)); + + for (size_t i = 0; i < m_entries.size(); i++) + Config::write_string(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, sanitize_name(m_entries[i]->name()), entry_to_config_string(i, m_entries[i])); } } diff --git a/Userland/Services/Taskbar/QuickLaunchWidget.h b/Userland/Services/Taskbar/QuickLaunchWidget.h index af7f22d2909..9cd2970c128 100644 --- a/Userland/Services/Taskbar/QuickLaunchWidget.h +++ b/Userland/Services/Taskbar/QuickLaunchWidget.h @@ -20,14 +20,16 @@ namespace Taskbar { class QuickLaunchEntry { public: + static OwnPtr create_from_config_value(StringView path); + static OwnPtr create_from_path(StringView path); + virtual ~QuickLaunchEntry() = default; virtual ErrorOr launch() const = 0; virtual GUI::Icon icon() const = 0; virtual DeprecatedString name() const = 0; virtual DeprecatedString file_name_to_watch() const = 0; - static OwnPtr create_from_config_value(StringView path); - static OwnPtr create_from_path(StringView path); + virtual DeprecatedString path() = 0; bool is_hovered() const { return m_hovered; } void set_hovered(bool hovered) { m_hovered = hovered; } @@ -52,6 +54,8 @@ public: virtual DeprecatedString name() const override { return m_app_file->name(); } virtual DeprecatedString file_name_to_watch() const override { return {}; } + virtual DeprecatedString path() override { return m_app_file->filename(); } + private: NonnullRefPtr m_app_file; }; @@ -68,6 +72,8 @@ public: virtual DeprecatedString name() const override; virtual DeprecatedString file_name_to_watch() const override { return m_path; } + virtual DeprecatedString path() override { return m_path; } + private: DeprecatedString m_path; }; @@ -83,6 +89,8 @@ public: virtual DeprecatedString name() const override; virtual DeprecatedString file_name_to_watch() const override { return m_path; } + virtual DeprecatedString path() override { return m_path; } + private: DeprecatedString m_path; }; @@ -95,6 +103,8 @@ public: static ErrorOr> create(); virtual ~QuickLaunchWidget() override = default; + void load_entries(bool save = true); + virtual void config_key_was_removed(StringView, StringView, StringView) override; virtual void config_string_did_change(StringView, StringView, StringView, StringView) override; @@ -113,18 +123,22 @@ public: ErrorOr add_from_pid(pid_t pid); private: + static constexpr StringView CONFIG_DOMAIN = "Taskbar"sv; + static constexpr StringView CONFIG_GROUP_ENTRIES = "QuickLaunch_Entries"sv; + static constexpr int BUTTON_SIZE = 24; + explicit QuickLaunchWidget(); - ErrorOr add_or_adjust_button(DeprecatedString const&, NonnullOwnPtr); + ErrorOr add_or_adjust_button(DeprecatedString const&, NonnullOwnPtr, bool save = true); ErrorOr create_context_menu(); - void add_quick_launch_buttons(Vector> entries); + void add_quick_launch_buttons(Vector> entries, bool save = true); template void for_each_entry(Callback); void resize(); - void set_or_insert_entry(NonnullOwnPtr); - void remove_entry(DeprecatedString const&); + void set_or_insert_entry(NonnullOwnPtr, bool save = true); + void remove_entry(DeprecatedString const&, bool save = true); void recalculate_order(); bool m_dragging { false };