mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-10 13:00:29 +03:00
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:
parent
67b65dffa8
commit
cea7dbce42
Notes:
sideshowbarker
2024-07-18 17:26:04 +09:00
Author: https://github.com/mattco98 Commit: https://github.com/SerenityOS/serenity/commit/cea7dbce42e Pull-request: https://github.com/SerenityOS/serenity/pull/7436 Reviewed-by: https://github.com/alimpfard
@ -1,6 +1,8 @@
|
||||
set(SOURCES
|
||||
OutlineModel.cpp
|
||||
PDFViewer.cpp
|
||||
PDFViewerWidget.cpp
|
||||
SidebarWidget.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
|
105
Userland/Applications/PDFViewer/OutlineModel.cpp
Normal file
105
Userland/Applications/PDFViewer/OutlineModel.cpp
Normal 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]);
|
||||
}
|
33
Userland/Applications/PDFViewer/OutlineModel.h
Normal file
33
Userland/Applications/PDFViewer/OutlineModel.h
Normal 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;
|
||||
};
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
31
Userland/Applications/PDFViewer/SidebarWidget.cpp
Normal file
31
Userland/Applications/PDFViewer/SidebarWidget.cpp
Normal 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
|
||||
}
|
35
Userland/Applications/PDFViewer/SidebarWidget.h
Normal file
35
Userland/Applications/PDFViewer/SidebarWidget.h
Normal 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;
|
||||
};
|
Loading…
Reference in New Issue
Block a user