GTK docking interfaces and more

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