PDFViewer: Add a tab bar with outlines and thumbnails

Outlines are in theory implemented (though I'm having trouble finding
a simple PDF with outlines to test it on), and thumbnails are not.
This commit is contained in:
Matthew Olsson 2021-05-23 21:34:06 -07:00 committed by Ali Mohammad Pur
parent 67b65dffa8
commit cea7dbce42
Notes: sideshowbarker 2024-07-18 17:26:04 +09:00
7 changed files with 250 additions and 7 deletions

View File

@ -1,6 +1,8 @@
set(SOURCES
OutlineModel.cpp
PDFViewer.cpp
PDFViewerWidget.cpp
SidebarWidget.cpp
main.cpp
)

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "OutlineModel.h"
#include <LibGfx/FontDatabase.h>
NonnullRefPtr<OutlineModel> OutlineModel::create(const NonnullRefPtr<PDF::OutlineDict>& outline)
{
return adopt_ref(*new OutlineModel(outline));
}
OutlineModel::OutlineModel(const NonnullRefPtr<PDF::OutlineDict>& outline)
: m_outline(outline)
{
m_closed_item_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/book.png"));
m_open_item_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/book-open.png"));
}
void OutlineModel::set_index_open_state(const GUI::ModelIndex& index, bool is_open)
{
VERIFY(index.is_valid());
auto* outline_item = static_cast<PDF::OutlineItem*>(index.internal_data());
if (is_open) {
m_open_outline_items.set(outline_item);
} else {
m_open_outline_items.remove(outline_item);
}
}
int OutlineModel::row_count(const GUI::ModelIndex& index) const
{
if (!index.is_valid())
return m_outline->children.size();
auto outline_item = static_cast<PDF::OutlineItem*>(index.internal_data());
return static_cast<int>(outline_item->children.size());
}
int OutlineModel::column_count(const GUI::ModelIndex&) const
{
return 1;
}
GUI::Variant OutlineModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
{
VERIFY(index.is_valid());
auto outline_item = static_cast<PDF::OutlineItem*>(index.internal_data());
switch (role) {
case GUI::ModelRole::Display:
return outline_item->title;
case GUI::ModelRole::Icon:
if (m_open_outline_items.contains(outline_item))
return m_open_item_icon;
return m_closed_item_icon;
default:
return {};
}
}
void OutlineModel::update()
{
did_update();
}
GUI::ModelIndex OutlineModel::parent_index(const GUI::ModelIndex& index) const
{
if (!index.is_valid())
return {};
auto* outline_item = static_cast<PDF::OutlineItem*>(index.internal_data());
auto& parent = outline_item->parent;
if (!parent)
return {};
if (parent->parent) {
auto& grandparent = parent->parent;
for (size_t i = 0; i < grandparent->children.size(); i++) {
auto* sibling = &grandparent->children[i];
if (sibling == index.internal_data())
return create_index(static_cast<int>(i), 0, sibling);
}
} else {
for (size_t i = 0; i < m_outline->children.size(); i++) {
auto* sibling = &m_outline->children[i];
if (sibling == index.internal_data())
return create_index(static_cast<int>(i), 0, sibling);
}
}
VERIFY_NOT_REACHED();
}
GUI::ModelIndex OutlineModel::index(int row, int column, const GUI::ModelIndex& parent) const
{
if (!parent.is_valid())
return create_index(row, column, &m_outline->children[row]);
auto parent_outline_item = static_cast<PDF::OutlineItem*>(parent.internal_data());
return create_index(row, column, &parent_outline_item->children[row]);
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGUI/Model.h>
#include <LibGUI/TreeView.h>
#include <LibPDF/Document.h>
class OutlineModel final : public GUI::Model {
public:
static NonnullRefPtr<OutlineModel> create(const NonnullRefPtr<PDF::OutlineDict>& outline);
void set_index_open_state(const GUI::ModelIndex& index, bool is_open);
virtual int row_count(const GUI::ModelIndex&) const override;
virtual int column_count(const GUI::ModelIndex&) const override;
virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override;
virtual void update() override;
virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override;
virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex&) const override;
private:
OutlineModel(const NonnullRefPtr<PDF::OutlineDict>& outline);
GUI::Icon m_closed_item_icon;
GUI::Icon m_open_item_icon;
NonnullRefPtr<PDF::OutlineDict> m_outline;
HashTable<PDF::OutlineItem*> m_open_outline_items;
};

View File

@ -6,23 +6,24 @@
#include "PDFViewerWidget.h"
#include <LibCore/File.h>
#include <LibGUI/Action.h>
#include <LibGUI/Application.h>
#include <LibGUI/BoxLayout.h>
#include <LibGUI/FilePicker.h>
#include <LibGUI/Menu.h>
#include <LibGUI/Menubar.h>
#include <LibGUI/Splitter.h>
PDFViewerWidget::PDFViewerWidget()
{
set_fill_with_background_color(true);
set_layout<GUI::VerticalBoxLayout>();
m_viewer = add<PDFViewer>();
}
auto& splitter = add<GUI::HorizontalSplitter>();
PDFViewerWidget::~PDFViewerWidget()
{
m_sidebar = splitter.add<SidebarWidget>();
m_sidebar->set_fixed_width(0);
m_viewer = splitter.add<PDFViewer>();
}
void PDFViewerWidget::initialize_menubar(GUI::Menubar& menubar)
@ -36,6 +37,21 @@ void PDFViewerWidget::initialize_menubar(GUI::Menubar& menubar)
file_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) {
GUI::Application::the()->quit();
}));
auto& view_menu = menubar.add_menu("&View");
auto open_sidebar_action = GUI::Action::create(
"Open &Sidebar", { Mod_Ctrl, Key_O }, Gfx::Bitmap::load_from_file("/res/icons/16x16/sidebar.png"), [&](auto& action) {
m_sidebar_open = !m_sidebar_open;
m_sidebar->set_fixed_width(m_sidebar_open ? 0 : 200);
action.set_text(m_sidebar_open ? "Open &Sidebar" : "Close &Sidebar");
},
nullptr);
open_sidebar_action->set_enabled(false);
view_menu.add_action(open_sidebar_action);
m_open_outline_action = open_sidebar_action;
}
void PDFViewerWidget::open_file(const String& path)
@ -44,5 +60,19 @@ void PDFViewerWidget::open_file(const String& path)
auto file_result = Core::File::open(path, Core::OpenMode::ReadOnly);
VERIFY(!file_result.is_error());
m_buffer = file_result.value()->read_all();
m_viewer->set_document(adopt_ref(*new PDF::Document(m_buffer)));
auto document = adopt_ref(*new PDF::Document(m_buffer));
m_viewer->set_document(document);
if (document->outline()) {
auto outline = document->outline();
m_sidebar->set_outline(outline.release_nonnull());
m_sidebar->set_fixed_width(200);
m_sidebar_open = true;
m_open_outline_action->set_enabled(true);
} else {
m_sidebar->set_outline({});
m_sidebar->set_fixed_width(0);
m_sidebar_open = false;
m_open_outline_action->set_enabled(false);
}
}

View File

@ -7,21 +7,28 @@
#pragma once
#include "PDFViewer.h"
#include "SidebarWidget.h"
#include <LibGUI/Action.h>
#include <LibGUI/Widget.h>
class PDFViewer;
class PDFViewerWidget final : public GUI::Widget {
C_OBJECT(PDFViewerWidget)
public:
~PDFViewerWidget() override;
~PDFViewerWidget() override = default;
void open_file(const String& path);
void initialize_menubar(GUI::Menubar&);
private:
PDFViewerWidget();
RefPtr<GUI::Action> m_open_outline_action;
RefPtr<PDFViewer> m_viewer;
RefPtr<SidebarWidget> m_sidebar;
bool m_sidebar_open { false };
ByteBuffer m_buffer;
RefPtr<GUI::Action> m_open_action;
};

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "SidebarWidget.h"
#include <LibGUI/BoxLayout.h>
#include <LibGUI/TabWidget.h>
SidebarWidget::SidebarWidget()
{
set_fill_with_background_color(true);
set_layout<GUI::VerticalBoxLayout>();
set_enabled(false);
auto& tab_bar = add<GUI::TabWidget>();
auto& outline_container = tab_bar.add_tab<GUI::Widget>("Outline");
outline_container.set_layout<GUI::VerticalBoxLayout>();
outline_container.layout()->set_margins({ 4, 4, 4, 4 });
m_outline_tree_view = outline_container.add<GUI::TreeView>();
m_outline_tree_view->set_activates_on_selection(true);
auto& thumbnails_container = tab_bar.add_tab<GUI::Widget>("Thumbnails");
thumbnails_container.set_layout<GUI::VerticalBoxLayout>();
thumbnails_container.layout()->set_margins({ 4, 4, 4, 4 });
// FIXME: Add thumbnail previews
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "OutlineModel.h"
#include <LibGUI/TreeView.h>
#include <LibGUI/Widget.h>
class SidebarWidget final : public GUI::Widget {
C_OBJECT(SidebarWidget)
public:
~SidebarWidget() override = default;
void set_outline(RefPtr<PDF::OutlineDict> outline)
{
if (outline) {
m_model = OutlineModel::create(outline.release_nonnull());
m_outline_tree_view->set_model(m_model);
} else {
m_model = RefPtr<OutlineModel> {};
m_outline_tree_view->set_model({});
}
}
private:
SidebarWidget();
RefPtr<OutlineModel> m_model;
RefPtr<GUI::TreeView> m_outline_tree_view;
};