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
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