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: "<index>:<path>". When
loading, we simply parse this structure out and sort by the index,
before parsing the path into `QuickLaunchEntry`s.
This commit is contained in:
david072 2023-11-05 12:13:19 +01:00 committed by Andreas Kling
parent 84340c4ca7
commit 13b98d01c0
Notes: sideshowbarker 2024-07-17 07:16:27 +09:00
3 changed files with 102 additions and 62 deletions

View File

@ -1,8 +1,8 @@
[Clock] [Clock]
TimeFormat=%T TimeFormat=%T
[QuickLaunch] [QuickLaunch_Entries]
Browser=Browser.af Browser=0:Browser.af
Terminal=Terminal.af FileManager=1:FileManager.af
FileManager=FileManager.af Terminal=2:Terminal.af
TextEditor=TextEditor.af TextEditor=3:TextEditor.af

View File

@ -26,8 +26,15 @@
namespace Taskbar { namespace Taskbar {
constexpr auto quick_launch = "QuickLaunch"sv; static DeprecatedString sanitize_name(DeprecatedString const& name)
constexpr int quick_launch_button_size = 24; {
return name.replace(" "sv, ""sv).replace("="sv, ""sv);
}
static DeprecatedString entry_to_config_string(size_t index, NonnullOwnPtr<QuickLaunchEntry> const& entry)
{
return DeprecatedString::formatted("{}:{}", index, entry->path());
}
ErrorOr<void> QuickLaunchEntryAppFile::launch() const ErrorOr<void> QuickLaunchEntryAppFile::launch() const
{ {
@ -88,20 +95,9 @@ DeprecatedString QuickLaunchEntryFile::name() const
ErrorOr<NonnullRefPtr<QuickLaunchWidget>> QuickLaunchWidget::create() ErrorOr<NonnullRefPtr<QuickLaunchWidget>> QuickLaunchWidget::create()
{ {
Vector<NonnullOwnPtr<QuickLaunchEntry>> 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())); auto widget = TRY(AK::adopt_nonnull_ref_or_enomem(new (nothrow) QuickLaunchWidget()));
TRY(widget->create_context_menu()); TRY(widget->create_context_menu());
widget->add_quick_launch_buttons(move(entries)); widget->load_entries();
return widget; return widget;
} }
@ -113,12 +109,44 @@ QuickLaunchWidget::QuickLaunchWidget()
set_fixed_height(24); set_fixed_height(24);
} }
void QuickLaunchWidget::load_entries(bool save)
{
struct ConfigEntry {
int index;
DeprecatedString path;
};
Vector<ConfigEntry> 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<NonnullOwnPtr<QuickLaunchEntry>> 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<void> QuickLaunchWidget::create_context_menu() ErrorOr<void> QuickLaunchWidget::create_context_menu()
{ {
auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/delete.png"sv)); auto icon = TRY(Gfx::Bitmap::load_from_file("/res/icons/16x16/delete.png"sv));
m_context_menu = GUI::Menu::construct(); m_context_menu = GUI::Menu::construct();
m_context_menu_default_action = GUI::Action::create("&Remove", icon, [this](auto&) { 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); remove_entry(m_context_menu_app_name);
resize(); resize();
update(); update();
@ -128,11 +156,14 @@ ErrorOr<void> QuickLaunchWidget::create_context_menu()
return {}; return {};
} }
void QuickLaunchWidget::add_quick_launch_buttons(Vector<NonnullOwnPtr<QuickLaunchEntry>> entries) void QuickLaunchWidget::add_quick_launch_buttons(Vector<NonnullOwnPtr<QuickLaunchEntry>> entries, bool save)
{ {
size_t size = entries.size(); 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)); 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(); resize();
update(); update();
@ -163,21 +194,15 @@ OwnPtr<QuickLaunchEntry> QuickLaunchEntry::create_from_path(StringView path)
return make<QuickLaunchEntryFile>(path); return make<QuickLaunchEntryFile>(path);
} }
static DeprecatedString sanitize_entry_name(DeprecatedString const& name) ErrorOr<void> QuickLaunchWidget::add_or_adjust_button(DeprecatedString const& button_name, NonnullOwnPtr<QuickLaunchEntry> entry, bool save)
{
return name.replace(" "sv, ""sv, ReplaceMode::All).replace("="sv, ""sv, ReplaceMode::All);
}
ErrorOr<void> QuickLaunchWidget::add_or_adjust_button(DeprecatedString const& button_name, NonnullOwnPtr<QuickLaunchEntry> entry)
{ {
auto file_name_to_watch = entry->file_name_to_watch(); auto file_name_to_watch = entry->file_name_to_watch();
if (!file_name_to_watch.is_empty()) { if (!file_name_to_watch.is_empty()) {
if (!m_watcher) { if (!m_watcher) {
m_watcher = TRY(Core::FileWatcher::create()); m_watcher = TRY(Core::FileWatcher::create());
m_watcher->on_change = [button_name, this](Core::FileWatcherEvent const& event) { m_watcher->on_change = [button_name, save, this](Core::FileWatcherEvent const&) {
auto name = sanitize_entry_name(event.event_path); dbgln("Removing QuickLaunch entry \"{}\"", button_name);
dbgln("Removing QuickLaunch entry {}", name); remove_entry(button_name, save);
remove_entry(button_name);
resize(); resize();
update(); update();
}; };
@ -185,7 +210,7 @@ ErrorOr<void> QuickLaunchWidget::add_or_adjust_button(DeprecatedString const& bu
TRY(m_watcher->add_watch(file_name_to_watch, Core::FileWatcherEvent::Type::Deleted)); 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(); resize();
update(); update();
@ -194,20 +219,14 @@ ErrorOr<void> QuickLaunchWidget::add_or_adjust_button(DeprecatedString const& bu
void QuickLaunchWidget::config_key_was_removed(StringView domain, StringView group, StringView key) void QuickLaunchWidget::config_key_was_removed(StringView domain, StringView group, StringView key)
{ {
if (domain == "Taskbar" && group == quick_launch) if (domain == "Taskbar" && group == CONFIG_GROUP_ENTRIES)
remove_entry(key); 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) { if (domain == "Taskbar" && group == CONFIG_GROUP_ENTRIES)
auto entry = QuickLaunchEntry::create_from_config_value(value); load_entries(false);
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()));
}
} }
void QuickLaunchWidget::drag_enter_event(GUI::DragEvent& event) 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 path = url.serialize_path();
auto entry = QuickLaunchEntry::create_from_path(path); auto entry = QuickLaunchEntry::create_from_path(path);
if (entry) { if (entry) {
auto item_name = sanitize_entry_name(entry->name()); auto result = add_or_adjust_button(entry->name(), entry.release_nonnull());
auto result = add_or_adjust_button(item_name, entry.release_nonnull());
if (result.is_error()) if (result.is_error())
GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Failed to add quick launch entry: {}", result.release_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<typename Callback> template<typename Callback>
void QuickLaunchWidget::for_each_entry(Callback callback) 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) { for (auto const& entry : m_entries) {
callback(entry, rect); callback(entry, rect);
rect.translate_by(quick_launch_button_size, 0); rect.translate_by(BUTTON_SIZE, 0);
} }
} }
@ -397,32 +414,38 @@ ErrorOr<bool> QuickLaunchWidget::add_from_pid(pid_t pid_to_add)
void QuickLaunchWidget::resize() 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<QuickLaunchEntry> entry) void QuickLaunchWidget::set_or_insert_entry(NonnullOwnPtr<QuickLaunchEntry> entry, bool save)
{ {
auto name = entry->name(); 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) if (value->name() != name)
continue; continue;
value = move(entry); value = move(entry);
if (save)
Config::write_string(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, sanitize_name(value->name()), entry_to_config_string(i, value));
return; 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)); 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++) { for (size_t i = 0; i < m_entries.size(); i++) {
if (m_entries[i]->name() != name) if (m_entries[i]->name() != name)
continue; continue;
if (save)
Config::remove_key(CONFIG_DOMAIN, CONFIG_GROUP_ENTRIES, m_entries[i]->name());
m_entries.remove(i); m_entries.remove(i);
return; return;
} }
} }
void QuickLaunchWidget::recalculate_order() void QuickLaunchWidget::recalculate_order()
{ {
if (!m_dragging) if (!m_dragging)
@ -435,7 +458,7 @@ void QuickLaunchWidget::recalculate_order()
} }
size_t new_index = m_entries.size() + 1; 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++) { for (size_t i = 0; i < m_entries.size(); i++) {
auto left_break_point = i == 0 ? rect.x() + rect.width() / 2 : rect.x(); auto left_break_point = i == 0 ? rect.x() + rect.width() / 2 : rect.x();
if (m_mouse_pos.x() < left_break_point) { if (m_mouse_pos.x() < left_break_point) {
@ -448,7 +471,7 @@ void QuickLaunchWidget::recalculate_order()
break; 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) 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); auto entry = m_entries.take(dragged_index);
m_entries.insert(new_index, move(entry)); 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]));
} }
} }

View File

@ -20,14 +20,16 @@ namespace Taskbar {
class QuickLaunchEntry { class QuickLaunchEntry {
public: public:
static OwnPtr<QuickLaunchEntry> create_from_config_value(StringView path);
static OwnPtr<QuickLaunchEntry> create_from_path(StringView path);
virtual ~QuickLaunchEntry() = default; virtual ~QuickLaunchEntry() = default;
virtual ErrorOr<void> launch() const = 0; virtual ErrorOr<void> launch() const = 0;
virtual GUI::Icon icon() const = 0; virtual GUI::Icon icon() const = 0;
virtual DeprecatedString name() const = 0; virtual DeprecatedString name() const = 0;
virtual DeprecatedString file_name_to_watch() const = 0; virtual DeprecatedString file_name_to_watch() const = 0;
static OwnPtr<QuickLaunchEntry> create_from_config_value(StringView path); virtual DeprecatedString path() = 0;
static OwnPtr<QuickLaunchEntry> create_from_path(StringView path);
bool is_hovered() const { return m_hovered; } bool is_hovered() const { return m_hovered; }
void set_hovered(bool hovered) { m_hovered = 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 name() const override { return m_app_file->name(); }
virtual DeprecatedString file_name_to_watch() const override { return {}; } virtual DeprecatedString file_name_to_watch() const override { return {}; }
virtual DeprecatedString path() override { return m_app_file->filename(); }
private: private:
NonnullRefPtr<Desktop::AppFile> m_app_file; NonnullRefPtr<Desktop::AppFile> m_app_file;
}; };
@ -68,6 +72,8 @@ public:
virtual DeprecatedString name() const override; virtual DeprecatedString name() const override;
virtual DeprecatedString file_name_to_watch() const override { return m_path; } virtual DeprecatedString file_name_to_watch() const override { return m_path; }
virtual DeprecatedString path() override { return m_path; }
private: private:
DeprecatedString m_path; DeprecatedString m_path;
}; };
@ -83,6 +89,8 @@ public:
virtual DeprecatedString name() const override; virtual DeprecatedString name() const override;
virtual DeprecatedString file_name_to_watch() const override { return m_path; } virtual DeprecatedString file_name_to_watch() const override { return m_path; }
virtual DeprecatedString path() override { return m_path; }
private: private:
DeprecatedString m_path; DeprecatedString m_path;
}; };
@ -95,6 +103,8 @@ public:
static ErrorOr<NonnullRefPtr<QuickLaunchWidget>> create(); static ErrorOr<NonnullRefPtr<QuickLaunchWidget>> create();
virtual ~QuickLaunchWidget() override = default; virtual ~QuickLaunchWidget() override = default;
void load_entries(bool save = true);
virtual void config_key_was_removed(StringView, StringView, StringView) override; virtual void config_key_was_removed(StringView, StringView, StringView) override;
virtual void config_string_did_change(StringView, StringView, StringView, StringView) override; virtual void config_string_did_change(StringView, StringView, StringView, StringView) override;
@ -113,18 +123,22 @@ public:
ErrorOr<bool> add_from_pid(pid_t pid); ErrorOr<bool> add_from_pid(pid_t pid);
private: 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(); explicit QuickLaunchWidget();
ErrorOr<void> add_or_adjust_button(DeprecatedString const&, NonnullOwnPtr<QuickLaunchEntry>); ErrorOr<void> add_or_adjust_button(DeprecatedString const&, NonnullOwnPtr<QuickLaunchEntry>, bool save = true);
ErrorOr<void> create_context_menu(); ErrorOr<void> create_context_menu();
void add_quick_launch_buttons(Vector<NonnullOwnPtr<QuickLaunchEntry>> entries); void add_quick_launch_buttons(Vector<NonnullOwnPtr<QuickLaunchEntry>> entries, bool save = true);
template<typename Callback> template<typename Callback>
void for_each_entry(Callback); void for_each_entry(Callback);
void resize(); void resize();
void set_or_insert_entry(NonnullOwnPtr<QuickLaunchEntry>); void set_or_insert_entry(NonnullOwnPtr<QuickLaunchEntry>, bool save = true);
void remove_entry(DeprecatedString const&); void remove_entry(DeprecatedString const&, bool save = true);
void recalculate_order(); void recalculate_order();
bool m_dragging { false }; bool m_dragging { false };