GTK docking interfaces and more

Important information: Google announced that, from September 2026, Android devices will require ALL apps to be signed by Google, effectively leading to an iOS situation. Value your right to a computer that does what you want; do not tolerate this monopolistic practice! Contact me if you don't understand why it is bad. Click to learn more.

 gpanthera.cc

View raw Download
text/x-c++ • 4.83 kiB
C++ source, ASCII text
        
            
1
#include "gpanthera.hh"
2
#include <iostream>
3
4
namespace gPanthera {
5
DockablePane::DockablePane(Gtk::Widget &child, const Glib::ustring &name, const Glib::ustring &label, Gtk::Image *icon, DockStack *stack, Gtk::Widget *custom_header)
6
: Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0), name(name) {
7
if(icon) {
8
this->icon = icon;
9
}
10
if(stack) {
11
this->stack = stack;
12
}
13
this->label.set_text(label);
14
header = Gtk::make_managed<Gtk::HeaderBar>();
15
header->set_show_close_button(false);
16
if(custom_header) {
17
header->set_custom_title(*custom_header);
18
} else {
19
header->set_custom_title(this->label);
20
}
21
auto header_menu_button = Gtk::make_managed<Gtk::MenuButton>();
22
auto header_menu = Gtk::make_managed<Gtk::Menu>();
23
header_menu_button->set_direction(Gtk::ARROW_NONE);
24
header_menu_button->set_popup(*header_menu);
25
auto close_menu_item = Gtk::make_managed<Gtk::MenuItem>("Close");
26
auto pop_out_menu_item = Gtk::make_managed<Gtk::MenuItem>("Pop out");
27
close_menu_item->signal_activate().connect([this]() {
28
if(this->stack) {
29
this->stack->set_visible_child("empty");
30
}
31
});
32
pop_out_menu_item->signal_activate().connect([this]() {
33
if(this->stack) {
34
this->pop_out();
35
}
36
});
37
header_menu->append(*close_menu_item);
38
header_menu->append(*pop_out_menu_item);
39
header_menu->show_all();
40
header->pack_end(*header_menu_button);
41
this->pack_start(*header, Gtk::PACK_SHRINK);
42
this->child = &child;
43
this->pack_end(child, Gtk::PACK_EXPAND_WIDGET);
44
show_all_children();
45
}
46
47
void DockablePane::redock(DockStack *stack) {
48
if(this->window != nullptr) {
49
this->window->close();
50
delete this->window;
51
this->window = nullptr;
52
}
53
this->stack = stack;
54
}
55
56
void DockablePane::pop_out() {
57
if(this->stack != nullptr) {
58
this->stack->remove(*this);
59
this->stack = nullptr;
60
}
61
62
if(this->window == nullptr) {
63
this->remove(*this->header);
64
this->window = new DockWindow(this);
65
this->window->set_titlebar(*this->header);
66
this->window->show_all();
67
}
68
}
69
70
Glib::ustring DockablePane::get_identifier() const {
71
return name;
72
}
73
74
Gtk::Image *DockablePane::get_icon() const {
75
return icon;
76
}
77
78
Gtk::HeaderBar *DockablePane::get_header() const {
79
return header;
80
}
81
82
Gtk::Widget *DockablePane::get_child() const {
83
return child;
84
}
85
86
Gtk::Label *DockablePane::get_label() {
87
return &label;
88
}
89
90
LayoutManager::LayoutManager() : Glib::ObjectBase("LayoutManager") {
91
}
92
93
void LayoutManager::add_pane(DockablePane *pane) {
94
panes.push_back(pane);
95
}
96
97
DockStack::DockStack(std::shared_ptr<LayoutManager> layout) : Gtk::Stack(), layout(layout) {
98
auto *empty_child = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_VERTICAL, 0);
99
add(*empty_child, "empty");
100
}
101
102
DockWindow::DockWindow(DockablePane *pane) {
103
this->pane = pane;
104
this->add(*pane);
105
}
106
107
DockStackSwitcher::DockStackSwitcher(DockStack *stack) : Gtk::ButtonBox(Gtk::ORIENTATION_HORIZONTAL), stack(stack) {
108
this->stack->signal_add().connect([this](Gtk::Widget *child) {
109
this->update_buttons();
110
});
111
this->stack->signal_remove().connect([this](Gtk::Widget *child) {
112
this->update_buttons();
113
});
114
}
115
116
void DockStackSwitcher::update_buttons() {
117
for(auto &child : get_children()) {
118
if(child) {
119
remove(*child);
120
}
121
}
122
for(auto const &widget : stack->get_children()) {
123
if(auto pane = dynamic_cast<DockablePane*>(widget)) {
124
auto *button = Gtk::make_managed<Gtk::Button>();
125
if(pane->get_icon()) {
126
button->set_image(*pane->get_icon());
127
}
128
button->signal_clicked().connect([this, pane]() {
129
if(stack->get_visible_child_name() == pane->get_identifier()) {
130
stack->set_visible_child("empty");
131
} else {
132
stack->set_visible_child(pane->get_identifier());
133
}
134
});
135
if(pane->get_identifier() != Glib::ustring("empty")) {
136
pack_start(*button);
137
}
138
}
139
}
140
}
141
142
void DockStack::add_pane(Gtk::Widget &child, const Glib::ustring &name) {
143
if(auto pane = dynamic_cast<DockablePane*>(&child)) {
144
pane->redock(this);
145
}
146
Gtk::Stack::add(child, name);
147
}
148
} // namespace gPanthera
149