diff options
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibGUI/Breadcrumbbar.h | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/Forward.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/PathBreadcrumbbar.cpp | 178 | ||||
-rw-r--r-- | Userland/Libraries/LibGUI/PathBreadcrumbbar.h | 45 |
5 files changed, 228 insertions, 2 deletions
diff --git a/Userland/Libraries/LibGUI/Breadcrumbbar.h b/Userland/Libraries/LibGUI/Breadcrumbbar.h index 96e11bd84e..2122ec1a7b 100644 --- a/Userland/Libraries/LibGUI/Breadcrumbbar.h +++ b/Userland/Libraries/LibGUI/Breadcrumbbar.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> * Copyright (c) 2022, the SerenityOS developers. + * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -40,10 +41,9 @@ public: protected: virtual void did_change_font() override; - -private: Breadcrumbbar(); +private: virtual void resize_event(ResizeEvent&) override; struct Segment { diff --git a/Userland/Libraries/LibGUI/CMakeLists.txt b/Userland/Libraries/LibGUI/CMakeLists.txt index f374709c38..7cc61bd0a4 100644 --- a/Userland/Libraries/LibGUI/CMakeLists.txt +++ b/Userland/Libraries/LibGUI/CMakeLists.txt @@ -82,6 +82,7 @@ set(SOURCES OpacitySlider.cpp Painter.cpp PasswordInputDialog.cpp + PathBreadcrumbbar.cpp PersistentModelIndex.cpp Process.cpp ProcessChooser.cpp diff --git a/Userland/Libraries/LibGUI/Forward.h b/Userland/Libraries/LibGUI/Forward.h index b6b5fd6786..a015e784c0 100644 --- a/Userland/Libraries/LibGUI/Forward.h +++ b/Userland/Libraries/LibGUI/Forward.h @@ -18,6 +18,7 @@ class Application; class AutocompleteBox; class AutocompleteProvider; class BoxLayout; +class Breadcrumbbar; class Button; class CheckBox; class ComboBox; @@ -56,6 +57,7 @@ class MultiView; class OpacitySlider; class PaintEvent; class Painter; +class PathBreadcrumbbar; class PersistentHandle; class PersistentModelIndex; class RadioButton; diff --git a/Userland/Libraries/LibGUI/PathBreadcrumbbar.cpp b/Userland/Libraries/LibGUI/PathBreadcrumbbar.cpp new file mode 100644 index 0000000000..f44b472065 --- /dev/null +++ b/Userland/Libraries/LibGUI/PathBreadcrumbbar.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org> + * Copyright (c) 2021, Mustafa Quraish <mustafa@cs.toronto.edu> + * Copyright (c) 2022, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "PathBreadcrumbbar.h" +#include <AK/LexicalPath.h> +#include <LibCore/DeprecatedFile.h> +#include <LibCore/MimeData.h> +#include <LibGUI/BoxLayout.h> +#include <LibGUI/Breadcrumbbar.h> +#include <LibGUI/FileIconProvider.h> +#include <LibGUI/Icon.h> +#include <LibGUI/TextBox.h> + +REGISTER_WIDGET(GUI, PathBreadcrumbbar) + +namespace GUI { + +ErrorOr<NonnullRefPtr<PathBreadcrumbbar>> PathBreadcrumbbar::try_create() +{ + auto location_text_box = TRY(TextBox::try_create()); + auto breadcrumbbar = TRY(Breadcrumbbar::try_create()); + + auto path_breadcrumbbar = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PathBreadcrumbbar(*location_text_box, *breadcrumbbar))); + (void)TRY(path_breadcrumbbar->try_set_layout<GUI::VerticalBoxLayout>()); + TRY(path_breadcrumbbar->try_add_child(location_text_box)); + TRY(path_breadcrumbbar->try_add_child(breadcrumbbar)); + + return path_breadcrumbbar; +} + +PathBreadcrumbbar::PathBreadcrumbbar(NonnullRefPtr<GUI::TextBox> location_text_box, NonnullRefPtr<GUI::Breadcrumbbar> breadcrumbbar) + : Widget() + , m_location_text_box(move(location_text_box)) + , m_breadcrumbbar(move(breadcrumbbar)) +{ + m_location_text_box->set_visible(false); + + m_location_text_box->on_escape_pressed = [&] { + hide_location_text_box(); + }; + m_location_text_box->on_focusout = [&] { + hide_location_text_box(); + }; + + m_location_text_box->on_return_pressed = [&] { + if (Core::DeprecatedFile::is_directory(m_location_text_box->text())) { + set_current_path(m_location_text_box->text()); + hide_location_text_box(); + } + }; + + m_breadcrumbbar->set_visible(true); + + m_breadcrumbbar->on_segment_change = [&](Optional<size_t> segment_index) { + if (!segment_index.has_value()) + return; + + if (!on_path_change) + return; + + auto segment_path = m_breadcrumbbar->segment_data(*segment_index); + on_path_change(segment_path); + }; + + m_breadcrumbbar->on_segment_drag_enter = [&](size_t, GUI::DragEvent& event) { + if (event.mime_types().contains_slow("text/uri-list")) + event.accept(); + }; + + m_breadcrumbbar->on_segment_drop = [&](size_t segment_index, GUI::DropEvent const& event) { + if (!event.mime_data().has_urls()) + return; + if (on_paths_drop) + on_paths_drop(m_breadcrumbbar->segment_data(segment_index), event); + }; + + m_breadcrumbbar->on_doubleclick = [&](GUI::MouseEvent const&) { + show_location_text_box(); + }; +} + +PathBreadcrumbbar::~PathBreadcrumbbar() = default; + +void PathBreadcrumbbar::set_current_path(DeprecatedString const& new_path) +{ + if (new_path == m_current_path) + return; + + LexicalPath lexical_path(new_path); + m_current_path = new_path; + + auto segment_index_of_new_path_in_breadcrumbbar = m_breadcrumbbar->find_segment_with_data(new_path); + + if (segment_index_of_new_path_in_breadcrumbbar.has_value()) { + auto new_segment_index = segment_index_of_new_path_in_breadcrumbbar.value(); + m_breadcrumbbar->set_selected_segment(new_segment_index); + + // If the path change was because the directory we were in was deleted, + // remove the breadcrumbs for it. + if ((new_segment_index + 1 < m_breadcrumbbar->segment_count()) + && !Core::DeprecatedFile::is_directory(m_breadcrumbbar->segment_data(new_segment_index + 1))) { + m_breadcrumbbar->remove_end_segments(new_segment_index + 1); + } + } else { + m_breadcrumbbar->clear_segments(); + + m_breadcrumbbar->append_segment("/", GUI::FileIconProvider::icon_for_path("/").bitmap_for_size(16), "/", "/"); + StringBuilder builder; + + for (auto& part : lexical_path.parts()) { + // NOTE: We rebuild the path as we go, so we have something to pass to GUI::FileIconProvider. + builder.append('/'); + builder.append(part); + + m_breadcrumbbar->append_segment(part, GUI::FileIconProvider::icon_for_path(builder.string_view()).bitmap_for_size(16), builder.string_view(), builder.string_view()); + } + + m_breadcrumbbar->set_selected_segment(m_breadcrumbbar->segment_count() - 1); + } +} + +bool PathBreadcrumbbar::has_parent_segment() const +{ + return m_breadcrumbbar->has_parent_segment(); +} + +bool PathBreadcrumbbar::has_child_segment() const +{ + return m_breadcrumbbar->has_child_segment(); +} + +void PathBreadcrumbbar::select_parent_segment() +{ + if (!has_parent_segment()) + return; + m_breadcrumbbar->set_selected_segment(m_breadcrumbbar->selected_segment().value() - 1); +} + +void PathBreadcrumbbar::select_child_segment() +{ + if (!has_child_segment()) + return; + m_breadcrumbbar->set_selected_segment(m_breadcrumbbar->selected_segment().value() + 1); +} + +void PathBreadcrumbbar::show_location_text_box() +{ + if (m_location_text_box->is_visible()) + return; + m_location_text_box->set_visible(true); + m_breadcrumbbar->set_visible(false); + + m_location_text_box->set_icon(GUI::FileIconProvider::icon_for_path(m_current_path).bitmap_for_size(16)); + m_location_text_box->set_text(m_current_path); + m_location_text_box->select_all(); + m_location_text_box->set_focus(true); +} + +void PathBreadcrumbbar::hide_location_text_box() +{ + if (!m_location_text_box->is_visible()) + return; + m_location_text_box->set_visible(false); + m_breadcrumbbar->set_visible(true); + + m_location_text_box->set_focus(false); + + if (on_hide_location_box) + on_hide_location_box(); +} + +} diff --git a/Userland/Libraries/LibGUI/PathBreadcrumbbar.h b/Userland/Libraries/LibGUI/PathBreadcrumbbar.h new file mode 100644 index 0000000000..1aa04f20f5 --- /dev/null +++ b/Userland/Libraries/LibGUI/PathBreadcrumbbar.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/NonnullRefPtr.h> +#include <LibGUI/Forward.h> +#include <LibGUI/Widget.h> + +namespace GUI { + +class PathBreadcrumbbar : public Widget { + C_OBJECT_ABSTRACT(PathBreadcrumbbar) +public: + static ErrorOr<NonnullRefPtr<PathBreadcrumbbar>> try_create(); + virtual ~PathBreadcrumbbar() override; + + void set_current_path(DeprecatedString const&); + + void show_location_text_box(); + void hide_location_text_box(); + + bool has_parent_segment() const; + bool has_child_segment() const; + + void select_parent_segment(); + void select_child_segment(); + + Function<void(StringView path)> on_path_change; + Function<void(StringView path, GUI::DropEvent const&)> on_paths_drop; + Function<void()> on_hide_location_box; + +private: + PathBreadcrumbbar(NonnullRefPtr<GUI::TextBox>, NonnullRefPtr<GUI::Breadcrumbbar>); + + NonnullRefPtr<GUI::TextBox> m_location_text_box; + NonnullRefPtr<GUI::Breadcrumbbar> m_breadcrumbbar; + + DeprecatedString m_current_path; +}; + +} |