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