mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 21:27:34 +00:00
LibGUI: Add PathBreadcrumbbar
This Widget wraps both a Breadcrumbbar and a TextBox for editing the path manually, based heavily on the existing code in FileManager. Breadcrumbbar itself requires outside code to micro-manage what it does. This class provides a simpler interface for it: Users don't have to worry about segments, they just give/receive a string for the current path.
This commit is contained in:
parent
f5cf41eb5d
commit
f0c2dcdbac
5 changed files with 228 additions and 2 deletions
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
|
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -40,10 +41,9 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void did_change_font() override;
|
virtual void did_change_font() override;
|
||||||
|
|
||||||
private:
|
|
||||||
Breadcrumbbar();
|
Breadcrumbbar();
|
||||||
|
|
||||||
|
private:
|
||||||
virtual void resize_event(ResizeEvent&) override;
|
virtual void resize_event(ResizeEvent&) override;
|
||||||
|
|
||||||
struct Segment {
|
struct Segment {
|
||||||
|
|
|
@ -82,6 +82,7 @@ set(SOURCES
|
||||||
OpacitySlider.cpp
|
OpacitySlider.cpp
|
||||||
Painter.cpp
|
Painter.cpp
|
||||||
PasswordInputDialog.cpp
|
PasswordInputDialog.cpp
|
||||||
|
PathBreadcrumbbar.cpp
|
||||||
PersistentModelIndex.cpp
|
PersistentModelIndex.cpp
|
||||||
Process.cpp
|
Process.cpp
|
||||||
ProcessChooser.cpp
|
ProcessChooser.cpp
|
||||||
|
|
|
@ -18,6 +18,7 @@ class Application;
|
||||||
class AutocompleteBox;
|
class AutocompleteBox;
|
||||||
class AutocompleteProvider;
|
class AutocompleteProvider;
|
||||||
class BoxLayout;
|
class BoxLayout;
|
||||||
|
class Breadcrumbbar;
|
||||||
class Button;
|
class Button;
|
||||||
class CheckBox;
|
class CheckBox;
|
||||||
class ComboBox;
|
class ComboBox;
|
||||||
|
@ -56,6 +57,7 @@ class MultiView;
|
||||||
class OpacitySlider;
|
class OpacitySlider;
|
||||||
class PaintEvent;
|
class PaintEvent;
|
||||||
class Painter;
|
class Painter;
|
||||||
|
class PathBreadcrumbbar;
|
||||||
class PersistentHandle;
|
class PersistentHandle;
|
||||||
class PersistentModelIndex;
|
class PersistentModelIndex;
|
||||||
class RadioButton;
|
class RadioButton;
|
||||||
|
|
178
Userland/Libraries/LibGUI/PathBreadcrumbbar.cpp
Normal file
178
Userland/Libraries/LibGUI/PathBreadcrumbbar.cpp
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
Userland/Libraries/LibGUI/PathBreadcrumbbar.h
Normal file
45
Userland/Libraries/LibGUI/PathBreadcrumbbar.h
Normal file
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue