gpanthera.cc
C++ source, ASCII text
1#include "gpanthera.hh" 2#include <iostream> 3#include <utility> 4#include <libintl.h> 5#include <locale.h> 6#include <filesystem> 7#define _(STRING) gettext(STRING) 8 9namespace gPanthera { 10std::vector<Gtk::Widget*> collect_children(Gtk::Widget &widget) { 11std::vector<Gtk::Widget*> children; 12for(auto *child = widget.get_first_child(); child; child = child->get_next_sibling()) { 13children.push_back(child); 14} 15return children; 16} 17 18void init() { 19setlocale(LC_ALL, "ro_RO.utf8"); 20 21bindtextdomain("gpanthera", "./locales"); 22textdomain("gpanthera"); 23} 24 25DockablePane::DockablePane(std::shared_ptr<LayoutManager> layout, Gtk::Widget &child, const Glib::ustring &name, const Glib::ustring &label, Gtk::Image *icon, DockStack *stack, Gtk::Widget *custom_header) 26: Gtk::Box(Gtk::Orientation::VERTICAL, 0), name(name) { 27if(icon) { 28this->icon = icon; 29} 30if(stack) { 31this->stack = stack; 32} 33this->layout = std::move(layout); 34this->label.set_text(label); 35header = std::make_unique<Gtk::HeaderBar>(); 36header->set_show_title_buttons(false); 37if(custom_header) { 38header->set_title_widget(*custom_header); 39} else { 40header->set_title_widget(this->label); 41} 42auto header_menu_button = Gtk::make_managed<Gtk::MenuButton>(); 43auto header_menu = Gio::Menu::create(); 44header_menu_button->set_direction(Gtk::ArrowType::NONE); 45header_menu_button->set_menu_model(header_menu); 46 47auto action_group = Gio::SimpleActionGroup::create(); 48header_menu_button->insert_action_group("win", action_group); 49 50// Close action 51auto close_action = Gio::SimpleAction::create("close"); 52close_action->signal_activate().connect([this](const Glib::VariantBase&) { 53if(this->stack) { 54this->stack->set_visible_child("empty"); 55} 56}); 57action_group->add_action(close_action); 58header_menu->append(_("Close"), "win.close"); 59 60// Pop out action 61auto pop_out_action = Gio::SimpleAction::create("pop_out"); 62pop_out_action->signal_activate().connect([this](const Glib::VariantBase&) { 63if(this->stack) { 64this->pop_out(); 65} 66}); 67action_group->add_action(pop_out_action); 68header_menu->append(_("Pop out"), "win.pop_out"); 69 70// Move menu 71auto move_menu = Gio::Menu::create(); 72for(auto &this_stack : this->layout->stacks) { 73auto action_name = "move_" + this_stack->name; 74auto move_action = Gio::SimpleAction::create(action_name); 75move_action->signal_activate().connect([this, this_stack](const Glib::VariantBase&) { 76this_stack->add_pane(*this); 77}); 78action_group->add_action(move_action); 79move_menu->append(this_stack->name, "win." + action_name); 80} 81 82// Add move section 83auto move_menu_item = Gio::Menu::create(); 84move_menu_item->append_section(_("Move"), move_menu); 85header_menu->append_section({}, move_menu_item); 86 87header->pack_end(*header_menu_button); 88 89this->prepend(*header); 90this->child = &child; 91this->append(child); 92} 93 94void DockablePane::redock(DockStack *stack) { 95if(this->window != nullptr) { 96this->window->hide(); 97this->window->unset_titlebar(); 98this->window->set_decorated(false); 99this->header->get_style_context()->remove_class("titlebar"); 100this->prepend(*this->header); 101this->window->unset_child(); 102this->window->close(); 103} else if(this->stack == this->get_parent()) { 104this->stack->remove(*this); 105} 106this->stack = stack; 107this->stack->add(*this, this->get_identifier()); 108if(this->window != nullptr) { 109this->window->destroy(); 110delete this->window; 111this->window = nullptr; 112} 113} 114 115void DockablePane::pop_out() { 116if(this->stack != nullptr) { 117this->stack->remove(*this); 118this->stack = nullptr; 119} 120 121if(this->window == nullptr) { 122this->remove(*this->header); 123this->window = new DockWindow(this); 124this->window->set_titlebar(*this->header); 125this->window->set_child(*this); 126this->window->set_decorated(true); 127this->window->present(); 128} 129} 130 131Glib::ustring DockablePane::get_identifier() const { 132return name; 133} 134 135Gtk::Image *DockablePane::get_icon() const { 136return icon; 137} 138 139Gtk::Widget *DockablePane::get_child() const { 140return child; 141} 142 143Gtk::Label *DockablePane::get_label() { 144return &label; 145} 146 147LayoutManager::LayoutManager() : Glib::ObjectBase("LayoutManager") { 148} 149 150void LayoutManager::add_pane(DockablePane *pane) { 151panes.push_back(pane); 152} 153 154void LayoutManager::add_stack(DockStack *stack) { 155stacks.push_back(stack); 156} 157 158DockStack::DockStack(std::shared_ptr<LayoutManager> layout, const Glib::ustring &name) : Gtk::Stack(), layout(layout), name(name) { 159auto *empty_child = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0); 160add(*empty_child, "empty"); 161// Add the stack to a layout manager 162this->layout->add_stack(this); 163} 164 165DockWindow::DockWindow(DockablePane *pane) { 166this->pane = pane; 167this->set_child(*pane); 168} 169 170DockStackSwitcher::DockStackSwitcher(DockStack *stack) : Gtk::Box(Gtk::Orientation::HORIZONTAL), stack(stack) { 171auto update_callback = [this](Gtk::Widget*) { 172this->update_buttons(); 173}; 174stack->signal_child_added.connect(update_callback); 175stack->signal_child_removed.connect(update_callback); 176} 177 178DockStackSwitcher::~DockStackSwitcher() { 179add_handler.disconnect(); 180remove_handler.disconnect(); 181} 182 183DockButton::DockButton(DockablePane *pane) : Gtk::Button(), pane(pane) { 184if(pane->get_icon()) { 185auto new_image = Gtk::make_managed<Gtk::Image>(this->pane->get_icon()->get_paintable()); 186this->set_child(*new_image); 187} 188this->set_tooltip_text(pane->get_label()->get_text()); 189this->set_halign(Gtk::Align::CENTER); 190} 191 192void DockStackSwitcher::update_buttons() { 193auto old_buttons = collect_children(*this); 194for(auto *button : old_buttons) { 195remove(*button); 196} 197for(auto *widget = stack->get_first_child(); widget; widget = widget->get_next_sibling()) { 198if(auto pane = dynamic_cast<DockablePane*>(widget)) { 199auto *button = Gtk::make_managed<DockButton>(pane); 200button->signal_clicked().connect([this, pane]() { 201if(stack->get_visible_child_name() == pane->get_identifier()) { 202stack->set_visible_child("empty"); 203} else { 204stack->set_visible_child(pane->get_identifier()); 205} 206}); 207if(pane->get_identifier() != Glib::ustring("empty")) { 208append(*button); 209} 210} 211} 212} 213 214void DockStack::add_pane(DockablePane &child) { 215child.redock(this); 216} 217 218void DockStack::add(Gtk::Widget &child, const Glib::ustring &name) { 219Gtk::Stack::add(child, name); 220signal_child_added.emit(&child); 221} 222 223void DockStack::remove(Gtk::Widget &child) { 224Gtk::Stack::remove(child); 225signal_child_removed.emit(&child); 226} 227} // namespace gPanthera 228