PathBreadcrumbbar.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /*
  2. * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
  4. * Copyright (c) 2021, Mustafa Quraish <mustafa@cs.toronto.edu>
  5. * Copyright (c) 2022, the SerenityOS developers.
  6. *
  7. * SPDX-License-Identifier: BSD-2-Clause
  8. */
  9. #include "PathBreadcrumbbar.h"
  10. #include <AK/LexicalPath.h>
  11. #include <LibCore/MimeData.h>
  12. #include <LibFileSystem/FileSystem.h>
  13. #include <LibGUI/BoxLayout.h>
  14. #include <LibGUI/Breadcrumbbar.h>
  15. #include <LibGUI/FileIconProvider.h>
  16. #include <LibGUI/Icon.h>
  17. #include <LibGUI/TextBox.h>
  18. REGISTER_WIDGET(GUI, PathBreadcrumbbar)
  19. namespace GUI {
  20. ErrorOr<NonnullRefPtr<PathBreadcrumbbar>> PathBreadcrumbbar::try_create()
  21. {
  22. auto location_text_box = TRY(TextBox::try_create());
  23. auto breadcrumbbar = TRY(Breadcrumbbar::try_create());
  24. auto path_breadcrumbbar = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PathBreadcrumbbar(*location_text_box, *breadcrumbbar)));
  25. (void)TRY(path_breadcrumbbar->try_set_layout<GUI::VerticalBoxLayout>());
  26. TRY(path_breadcrumbbar->try_add_child(location_text_box));
  27. TRY(path_breadcrumbbar->try_add_child(breadcrumbbar));
  28. return path_breadcrumbbar;
  29. }
  30. PathBreadcrumbbar::PathBreadcrumbbar(NonnullRefPtr<GUI::TextBox> location_text_box, NonnullRefPtr<GUI::Breadcrumbbar> breadcrumbbar)
  31. : Widget()
  32. , m_location_text_box(move(location_text_box))
  33. , m_breadcrumbbar(move(breadcrumbbar))
  34. {
  35. m_location_text_box->set_visible(false);
  36. m_location_text_box->on_escape_pressed = [&] {
  37. hide_location_text_box();
  38. };
  39. m_location_text_box->on_focusout = [&] {
  40. hide_location_text_box();
  41. };
  42. m_location_text_box->on_return_pressed = [&] {
  43. if (FileSystem::is_directory(m_location_text_box->text())) {
  44. set_current_path(m_location_text_box->text());
  45. hide_location_text_box();
  46. }
  47. };
  48. m_breadcrumbbar->set_visible(true);
  49. m_breadcrumbbar->on_segment_change = [&](Optional<size_t> segment_index) {
  50. if (!segment_index.has_value())
  51. return;
  52. if (!on_path_change)
  53. return;
  54. auto segment_path = m_breadcrumbbar->segment_data(*segment_index);
  55. on_path_change(segment_path);
  56. };
  57. m_breadcrumbbar->on_segment_drag_enter = [&](size_t, GUI::DragEvent& event) {
  58. if (event.mime_types().contains_slow("text/uri-list"))
  59. event.accept();
  60. };
  61. m_breadcrumbbar->on_segment_drop = [&](size_t segment_index, GUI::DropEvent const& event) {
  62. if (!event.mime_data().has_urls())
  63. return;
  64. if (on_paths_drop)
  65. on_paths_drop(m_breadcrumbbar->segment_data(segment_index), event);
  66. };
  67. m_breadcrumbbar->on_doubleclick = [&](auto) {
  68. show_location_text_box();
  69. };
  70. }
  71. PathBreadcrumbbar::~PathBreadcrumbbar() = default;
  72. void PathBreadcrumbbar::set_current_path(DeprecatedString const& new_path)
  73. {
  74. if (new_path == m_current_path)
  75. return;
  76. LexicalPath lexical_path(new_path);
  77. m_current_path = new_path;
  78. auto segment_index_of_new_path_in_breadcrumbbar = m_breadcrumbbar->find_segment_with_data(new_path);
  79. if (segment_index_of_new_path_in_breadcrumbbar.has_value()) {
  80. auto new_segment_index = segment_index_of_new_path_in_breadcrumbbar.value();
  81. m_breadcrumbbar->set_selected_segment(new_segment_index);
  82. // If the path change was because the directory we were in was deleted,
  83. // remove the breadcrumbs for it.
  84. if ((new_segment_index + 1 < m_breadcrumbbar->segment_count())
  85. && !FileSystem::is_directory(m_breadcrumbbar->segment_data(new_segment_index + 1))) {
  86. m_breadcrumbbar->remove_end_segments(new_segment_index + 1);
  87. }
  88. } else {
  89. m_breadcrumbbar->clear_segments();
  90. m_breadcrumbbar->append_segment("/", GUI::FileIconProvider::icon_for_path("/"sv).bitmap_for_size(16), "/", "/");
  91. StringBuilder builder;
  92. for (auto& part : lexical_path.parts()) {
  93. // NOTE: We rebuild the path as we go, so we have something to pass to GUI::FileIconProvider.
  94. builder.append('/');
  95. builder.append(part);
  96. m_breadcrumbbar->append_segment(part, GUI::FileIconProvider::icon_for_path(builder.string_view()).bitmap_for_size(16), builder.string_view(), builder.string_view());
  97. }
  98. m_breadcrumbbar->set_selected_segment(m_breadcrumbbar->segment_count() - 1);
  99. }
  100. }
  101. bool PathBreadcrumbbar::has_parent_segment() const
  102. {
  103. return m_breadcrumbbar->has_parent_segment();
  104. }
  105. bool PathBreadcrumbbar::has_child_segment() const
  106. {
  107. return m_breadcrumbbar->has_child_segment();
  108. }
  109. void PathBreadcrumbbar::select_parent_segment()
  110. {
  111. if (!has_parent_segment())
  112. return;
  113. m_breadcrumbbar->set_selected_segment(m_breadcrumbbar->selected_segment().value() - 1);
  114. }
  115. void PathBreadcrumbbar::select_child_segment()
  116. {
  117. if (!has_child_segment())
  118. return;
  119. m_breadcrumbbar->set_selected_segment(m_breadcrumbbar->selected_segment().value() + 1);
  120. }
  121. void PathBreadcrumbbar::show_location_text_box()
  122. {
  123. if (m_location_text_box->is_visible())
  124. return;
  125. m_location_text_box->set_visible(true);
  126. m_breadcrumbbar->set_visible(false);
  127. m_location_text_box->set_icon(GUI::FileIconProvider::icon_for_path(m_current_path).bitmap_for_size(16));
  128. m_location_text_box->set_text(m_current_path);
  129. m_location_text_box->select_all();
  130. m_location_text_box->set_focus(true);
  131. }
  132. void PathBreadcrumbbar::hide_location_text_box()
  133. {
  134. if (!m_location_text_box->is_visible())
  135. return;
  136. m_location_text_box->set_visible(false);
  137. m_breadcrumbbar->set_visible(true);
  138. m_location_text_box->set_focus(false);
  139. if (on_hide_location_box)
  140. on_hide_location_box();
  141. }
  142. }