mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 09:48:11 +00:00
Help: Add a search mode
This closes #2685, and brings some much needed nostalgia :^)
This commit is contained in:
parent
6131417904
commit
21094b4725
3 changed files with 104 additions and 16 deletions
|
@ -28,6 +28,9 @@
|
|||
#include "ManualNode.h"
|
||||
#include "ManualPageNode.h"
|
||||
#include "ManualSectionNode.h"
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibGUI/FilteringProxyModel.h>
|
||||
|
||||
static ManualSectionNode s_sections[] = {
|
||||
{ "1", "Command-line programs" },
|
||||
|
@ -76,6 +79,25 @@ String ManualModel::page_path(const GUI::ModelIndex& index) const
|
|||
return page->path();
|
||||
}
|
||||
|
||||
Result<StringView, int> ManualModel::page_view(const String& path) const
|
||||
{
|
||||
if (path.is_empty())
|
||||
return StringView {};
|
||||
|
||||
auto mapped_file = m_mapped_files.get(path);
|
||||
if (mapped_file.has_value())
|
||||
return StringView { (const char*)mapped_file.value()->data(), mapped_file.value()->size() };
|
||||
|
||||
auto map = make<MappedFile>(path);
|
||||
if (!map->is_valid())
|
||||
return map->errno_if_invalid();
|
||||
|
||||
StringView view { (const char*)map->data(), map->size() };
|
||||
m_mapped_files.set(path, move(map));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
String ManualModel::page_and_section(const GUI::ModelIndex& index) const
|
||||
{
|
||||
if (!index.is_valid())
|
||||
|
@ -137,6 +159,10 @@ GUI::Variant ManualModel::data(const GUI::ModelIndex& index, Role role) const
|
|||
{
|
||||
auto* node = static_cast<const ManualNode*>(index.internal_data());
|
||||
switch (role) {
|
||||
case Role::Search:
|
||||
if (!node->is_page())
|
||||
return {};
|
||||
return String(page_view(page_path(index)).value());
|
||||
case Role::Display:
|
||||
return node->name();
|
||||
case Role::Icon:
|
||||
|
@ -156,6 +182,15 @@ void ManualModel::update_section_node_on_toggle(const GUI::ModelIndex& index, co
|
|||
node->set_open(open);
|
||||
}
|
||||
|
||||
TriState ManualModel::data_matches(const GUI::ModelIndex& index, GUI::Variant term) const
|
||||
{
|
||||
auto view_result = page_view(page_path(index));
|
||||
if (view_result.is_error() || view_result.value().is_empty())
|
||||
return TriState::False;
|
||||
|
||||
return view_result.value().contains(term.as_string()) ? TriState::True : TriState::False;
|
||||
}
|
||||
|
||||
void ManualModel::update()
|
||||
{
|
||||
did_update();
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Result.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibGUI/Model.h>
|
||||
|
||||
|
@ -44,11 +45,13 @@ public:
|
|||
|
||||
String page_path(const GUI::ModelIndex&) const;
|
||||
String page_and_section(const GUI::ModelIndex&) const;
|
||||
Result<StringView, int> page_view(const String& path) const;
|
||||
|
||||
void update_section_node_on_toggle(const GUI::ModelIndex&, const bool);
|
||||
virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
||||
virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override;
|
||||
virtual GUI::Variant data(const GUI::ModelIndex&, Role = Role::Display) const override;
|
||||
virtual TriState data_matches(const GUI::ModelIndex&, GUI::Variant) const override;
|
||||
virtual void update() override;
|
||||
virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override;
|
||||
virtual GUI::ModelIndex index(int row, int column = 0, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override;
|
||||
|
@ -59,4 +62,5 @@ private:
|
|||
GUI::Icon m_section_open_icon;
|
||||
GUI::Icon m_section_icon;
|
||||
GUI::Icon m_page_icon;
|
||||
mutable HashMap<String, OwnPtr<MappedFile>> m_mapped_files;
|
||||
};
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
#include "History.h"
|
||||
#include "ManualModel.h"
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/URL.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibDesktop/Launcher.h>
|
||||
|
@ -34,11 +33,14 @@
|
|||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/FilteringProxyModel.h>
|
||||
#include <LibGUI/ListView.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/MenuBar.h>
|
||||
#include <LibGUI/MessageBox.h>
|
||||
#include <LibGUI/Splitter.h>
|
||||
#include <LibGUI/TextEditor.h>
|
||||
#include <LibGUI/TabWidget.h>
|
||||
#include <LibGUI/TextBox.h>
|
||||
#include <LibGUI/ToolBar.h>
|
||||
#include <LibGUI/ToolBarContainer.h>
|
||||
#include <LibGUI/TreeView.h>
|
||||
|
@ -100,10 +102,41 @@ int main(int argc, char* argv[])
|
|||
|
||||
auto model = ManualModel::create();
|
||||
|
||||
auto& tree_view = splitter.add<GUI::TreeView>();
|
||||
auto& left_tab_bar = splitter.add<GUI::TabWidget>();
|
||||
auto& tree_view = left_tab_bar.add_tab<GUI::TreeView>("Tree");
|
||||
auto& search_view = left_tab_bar.add_tab<GUI::Widget>("Search");
|
||||
search_view.set_layout<GUI::VerticalBoxLayout>();
|
||||
auto& search_box = search_view.add<GUI::TextBox>();
|
||||
auto& search_list_view = search_view.add<GUI::ListView>();
|
||||
search_box.set_preferred_size(0, 20);
|
||||
search_box.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed);
|
||||
search_box.set_text("Search...");
|
||||
search_box.on_focusin = [&] {
|
||||
if (search_box.text() == "Search...")
|
||||
search_box.set_text("");
|
||||
};
|
||||
search_box.on_change = [&] {
|
||||
if (auto model = search_list_view.model()) {
|
||||
auto& search_model = *static_cast<GUI::FilteringProxyModel*>(model);
|
||||
search_model.set_filter_term(search_box.text());
|
||||
search_model.update();
|
||||
}
|
||||
};
|
||||
search_box.on_focusout = [&] {
|
||||
if (search_box.text().is_empty()) {
|
||||
if (auto model = search_list_view.model()) {
|
||||
auto& search_model = *static_cast<GUI::FilteringProxyModel*>(model);
|
||||
search_model.set_filter_term("");
|
||||
}
|
||||
search_box.set_text("Search...");
|
||||
}
|
||||
};
|
||||
search_list_view.set_model(GUI::FilteringProxyModel::construct(model));
|
||||
search_list_view.model()->update();
|
||||
|
||||
tree_view.set_model(model);
|
||||
tree_view.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill);
|
||||
tree_view.set_preferred_size(200, 500);
|
||||
left_tab_bar.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill);
|
||||
left_tab_bar.set_preferred_size(200, 500);
|
||||
|
||||
auto& page_view = splitter.add<Web::PageView>();
|
||||
|
||||
|
@ -123,19 +156,13 @@ int main(int argc, char* argv[])
|
|||
return;
|
||||
}
|
||||
|
||||
dbg() << "Opening page at " << path;
|
||||
|
||||
auto file = Core::File::construct();
|
||||
file->set_filename(path);
|
||||
|
||||
if (!file->open(Core::IODevice::OpenMode::ReadOnly)) {
|
||||
int saved_errno = errno;
|
||||
GUI::MessageBox::show(window, strerror(saved_errno), "Failed to open man page", GUI::MessageBox::Type::Error);
|
||||
auto source_result = model->page_view(path);
|
||||
if (source_result.is_error()) {
|
||||
GUI::MessageBox::show(window, strerror(source_result.error()), "Failed to open man page", GUI::MessageBox::Type::Error);
|
||||
return;
|
||||
}
|
||||
auto buffer = file->read_all();
|
||||
StringView source { (const char*)buffer.data(), buffer.size() };
|
||||
|
||||
auto source = source_result.value();
|
||||
String html;
|
||||
{
|
||||
auto md_document = Markdown::Document::parse(source);
|
||||
|
@ -173,6 +200,28 @@ int main(int argc, char* argv[])
|
|||
GUI::MessageBox::Type::Error);
|
||||
}
|
||||
};
|
||||
search_list_view.on_selection = [&](auto index) {
|
||||
if (!index.is_valid())
|
||||
return;
|
||||
|
||||
if (auto model = search_list_view.model()) {
|
||||
auto& search_model = *static_cast<GUI::FilteringProxyModel*>(model);
|
||||
index = search_model.map(index);
|
||||
} else {
|
||||
page_view.set_document(nullptr);
|
||||
return;
|
||||
}
|
||||
String path = model->page_path(index);
|
||||
if (path.is_null()) {
|
||||
page_view.set_document(nullptr);
|
||||
return;
|
||||
}
|
||||
tree_view.selection().clear();
|
||||
tree_view.selection().add(index);
|
||||
history.push(path);
|
||||
update_actions();
|
||||
open_page(path);
|
||||
};
|
||||
|
||||
page_view.on_link_click = [&](auto& url, auto&, unsigned) {
|
||||
if (url.protocol() != "file") {
|
||||
|
@ -230,7 +279,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
app->set_menubar(move(menubar));
|
||||
|
||||
window->set_focused_widget(&tree_view);
|
||||
window->set_focused_widget(&left_tab_bar);
|
||||
window->show();
|
||||
|
||||
return app->exec();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue