roundabout,
created on Thursday, 24 April 2025, 15:44:22 (1745509462),
received on Thursday, 24 April 2025, 15:44:28 (1745509468)
Author identity: vlad <vlad.muntoiu@gmail.com>
121c4a47ed4b2ac0e04e855a29272b2f30e9091d
CMakeLists.txt
@@ -8,10 +8,12 @@ add_library(gpanthera SHARED gpanthera.cc)
add_executable(panthera-www panthera-www.cc)
target_link_libraries(panthera-www gpanthera)
target_link_libraries(panthera-www webkitgtk-6.0)
target_link_libraries(panthera-www nlohmann_json::nlohmann_json)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTKMM REQUIRED gtkmm-4.0)
pkg_check_modules(WebKit REQUIRED webkitgtk-6.0)
find_package(nlohmann_json 3.10.0 REQUIRED)
include_directories(${GTKMM_INCLUDE_DIRS})
link_directories(${GTKMM_LIBRARY_DIRS})
gpanthera.cc
@@ -7,6 +7,9 @@
#include <locale.h>
#include <filesystem>
#include <ranges>
#include <nlohmann/json.hpp>
#include <unordered_set>
#define _(STRING) gettext(STRING)
namespace gPanthera {
@@ -76,7 +79,8 @@ namespace gPanthera {
if(stack) {
this->stack = stack;
}
this->layout = std::move(layout);
this->layout = layout;
this->layout->add_pane(this);
this->label.set_text(label);
// This should be replaced with a custom class in the future
header = std::make_unique<Gtk::HeaderBar>();
@@ -172,6 +176,7 @@ namespace gPanthera {
action->set_enabled(true);
this->header->get_style_context()->remove_class("gpanthera-dock-titlebar-popout");
}
layout->signal_pane_moved.emit(this);
}
void DockablePane::pop_out() {
@@ -193,6 +198,7 @@ namespace gPanthera {
this->header->get_style_context()->add_class("gpanthera-dock-titlebar-popout");
}
this->window->present();
layout->signal_pane_moved.emit(this);
}
Glib::ustring DockablePane::get_identifier() const {
@@ -1063,4 +1069,48 @@ namespace gPanthera {
this->last_operated_page = page;
this->signal_page_operated.emit(page);
}
DockWindow *DockablePane::get_window() const {
return this->window;
}
std::string LayoutManager::get_layout_as_json() const {
nlohmann::json json;
std::unordered_set<DockablePane*> panes_set;
for(auto pane: panes) {
panes_set.insert(pane);
}
for(auto stack: stacks) {
for(auto pane: collect_children(*stack)) {
if(dynamic_cast<DockablePane*>(pane)) {
panes_set.erase(dynamic_cast<DockablePane*>(pane));
json[stack->id].push_back(dynamic_cast<DockablePane*>(pane)->get_identifier());
}
}
}
for(auto pane: panes_set) {
if(pane->get_window()) {
json[""].push_back(pane->get_identifier());
}
}
return json.dump();
}
void LayoutManager::restore_json_layout(const std::string &json_string) {
nlohmann::json json = nlohmann::json::parse(json_string);
for(auto stack: stacks) {
if(json.contains(stack->id)) {
for(auto const &pane_id: json[stack->id]) {
for(auto pane: panes) {
// TODO: could probably be done with a better complexity if there are
// many panes
if(pane->get_identifier() == static_cast<Glib::ustring>(pane_id.get<std::string>())) {
stack->add_pane(*pane);
break;
}
}
}
}
}
}
} // namespace gPanthera
gpanthera.hh
@@ -28,6 +28,7 @@ namespace gPanthera {
Gtk::Widget *child;
Glib::RefPtr<Gio::SimpleActionGroup> action_group;
public:
DockWindow *get_window() const;
std::shared_ptr<LayoutManager> layout;
Glib::RefPtr<Gio::Menu> header_menu = Gio::Menu::create();
DockStack *last_stack = nullptr;
@@ -53,7 +54,11 @@ namespace gPanthera {
public:
std::vector<DockablePane*> panes;
std::vector<DockStack*> stacks;
sigc::signal<void(DockablePane*)> signal_pane_moved;
LayoutManager();
std::string get_layout_as_json() const;
void restore_json_layout(const std::string& json_string);
void add_pane(DockablePane *pane);
panthera-www.cc
@@ -9,6 +9,7 @@
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <webkit/webkit.h>
#include <fstream>
class PantheraWww : public Gtk::Application {
Gtk::Window *window = Gtk::make_managed<Gtk::Window>();
@@ -78,10 +79,6 @@ protected:
dock_stack_2->set_transition_duration(125);
dock_stack_2->set_expand(true);
dock_stack_1->add_pane(*pane_1);
dock_stack_1->add_pane(*pane_3);
dock_stack_2->add_pane(*pane_2);
auto outer_grid = Gtk::make_managed<Gtk::Grid>();
outer_grid->attach(*switcher_2, 0, 1, 1, 1);
outer_grid->attach(*switcher_1, 1, 2, 1, 1);
@@ -139,7 +136,32 @@ protected:
debug_button->signal_clicked().connect([this]() {
std::cout << "Last operated page: " << content_manager->get_last_operated_page()->get_name() << std::endl;
});
// TODO: Use the last operated page and allow opening tabs next to the last operated page using panes
// TODO: Use the last operated page and allow opening tabs next to the last operated page using certain panes
// Load the existing layout, if it exists
std::ifstream layout_file_in("layout.json");
if(layout_file_in) {
std::string layout_json((std::istreambuf_iterator<char>(layout_file_in)), std::istreambuf_iterator<char>());
layout_file_in.close();
layout_manager->restore_json_layout(layout_json);
} else {
// Create a new layout if the file doesn't exist
layout_file_in.close();
dock_stack_1->add_pane(*pane_1);
dock_stack_1->add_pane(*pane_3);
dock_stack_2->add_pane(*pane_2);
std::ofstream layout_file_out("layout.json");
layout_file_out << layout_manager->get_layout_as_json();
layout_file_out.close();
}
// Save the layout when changed
layout_manager->signal_pane_moved.connect([this](gPanthera::DockablePane *pane) {
std::ofstream layout_file_out("layout.json");
layout_file_out << layout_manager->get_layout_as_json();
layout_file_out.close();
std::cout << "Layout changed: " << layout_manager->get_layout_as_json() << std::endl;
});
}
void on_activate() override {