GTK docking interfaces

By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 gpanthera.cc

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