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

 panthera-www.cc

View raw Download
text/x-c++ • 15.82 kiB
C++ source, ASCII text
        
            
1
#include "gpanthera.hh"
2
#include <gtkmm.h>
3
#include <glibmm.h>
4
#include <glibmm/ustring.h>
5
#include <iostream>
6
#include <memory>
7
#include <libintl.h>
8
#include <locale.h>
9
#include <gtk/gtk.h>
10
#include <gdk/gdk.h>
11
#include <webkit/webkit.h>
12
#include <fstream>
13
14
class WebPage : public Gtk::Box {
15
16
};
17
18
class PantheraWww : public Gtk::Application {
19
Gtk::Window *window = Gtk::make_managed<Gtk::Window>();
20
protected:
21
std::shared_ptr<gPanthera::LayoutManager> layout_manager;
22
std::shared_ptr<gPanthera::ContentManager> content_manager;
23
Gtk::Entry *url_bar = nullptr;
24
std::string cookie_file = "cookies.txt";
25
26
static void notify_callback(GObject *object, GParamSpec *pspec, gpointer data) {
27
if(!gtk_widget_get_parent(GTK_WIDGET(object))) {
28
return;
29
}
30
auto parent = gtk_widget_get_parent(gtk_widget_get_parent(GTK_WIDGET(object)));
31
if(auto page = dynamic_cast<gPanthera::ContentPage*>(Glib::wrap(parent))) {
32
if(g_strcmp0(pspec->name, "title") == 0) {
33
if(auto label = dynamic_cast<Gtk::Label*>(page->tab_widget)) {
34
label->set_label(webkit_web_view_get_title(WEBKIT_WEB_VIEW(object)));
35
}
36
}
37
}
38
}
39
40
static void notify_focused_callback(GObject *object, GParamSpec *pspec, gpointer data) {
41
if(!gtk_widget_get_parent(GTK_WIDGET(object))) {
42
return;
43
}
44
auto this_ = static_cast<PantheraWww*>(data);
45
auto parent = gtk_widget_get_parent(gtk_widget_get_parent(GTK_WIDGET(object)));
46
if(auto page = dynamic_cast<gPanthera::ContentPage*>(Glib::wrap(parent))) {
47
if(g_strcmp0(pspec->name, "uri") == 0) {
48
this_->url_bar->set_text(webkit_web_view_get_uri(WEBKIT_WEB_VIEW(object)));
49
}
50
}
51
}
52
53
static void on_back_pressed(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) {
54
WebKitWebView *webview = WEBKIT_WEB_VIEW(user_data);
55
webkit_web_view_go_back(webview);
56
}
57
58
static void on_forward_pressed(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) {
59
WebKitWebView *webview = WEBKIT_WEB_VIEW(user_data);
60
webkit_web_view_go_forward(webview);
61
}
62
63
void on_new_tab(gPanthera::ContentStack *stack) {
64
if(!stack) {
65
// Find the current area
66
stack = content_manager->get_last_operated_page()->get_stack();
67
}
68
69
WebKitWebView *webview = WEBKIT_WEB_VIEW(webkit_web_view_new());
70
gtk_widget_set_hexpand(GTK_WIDGET(webview), true);
71
gtk_widget_set_vexpand(GTK_WIDGET(webview), true);
72
auto page_content = Gtk::make_managed<Gtk::Box>();
73
gtk_box_append(page_content->gobj(), GTK_WIDGET(webview));
74
auto page_tab = new Gtk::Label("Untitled");
75
auto page = Gtk::make_managed<gPanthera::ContentPage>(content_manager, stack, page_content, page_tab);
76
g_signal_connect(webview, "notify", G_CALLBACK(notify_callback), page->gobj());
77
webkit_web_view_load_uri(webview, "about:blank");
78
auto cookie_manager = webkit_network_session_get_cookie_manager(webkit_web_view_get_network_session(webview));
79
webkit_cookie_manager_set_persistent_storage(cookie_manager, cookie_file.c_str(), WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT);
80
webkit_cookie_manager_set_accept_policy(cookie_manager, WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS);
81
GtkEventController *click_controller_back = GTK_EVENT_CONTROLLER(gtk_gesture_click_new());
82
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(click_controller_back), 8);
83
g_signal_connect(click_controller_back, "pressed", G_CALLBACK(on_back_pressed), webview);
84
gtk_widget_add_controller(GTK_WIDGET(webview), click_controller_back);
85
86
GtkEventController *click_controller_forward = GTK_EVENT_CONTROLLER(gtk_gesture_click_new());
87
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(click_controller_forward), 9);
88
g_signal_connect(click_controller_forward, "pressed", G_CALLBACK(on_forward_pressed), webview);
89
gtk_widget_add_controller(GTK_WIDGET(webview), click_controller_forward);
90
91
stack->add_page(*page);
92
stack->set_visible_child(*page);
93
content_manager->set_last_operated_page(page);
94
}
95
96
void on_startup() override {
97
Gtk::Application::on_startup();
98
add_window(*window);
99
window->set_default_size(600, 400);
100
layout_manager = std::make_shared<gPanthera::LayoutManager>();
101
auto dock_stack_1 = Gtk::make_managed<gPanthera::DockStack>(layout_manager, "One", "one");
102
auto switcher_1 = Gtk::make_managed<gPanthera::DockStackSwitcher>(dock_stack_1, Gtk::Orientation::HORIZONTAL);
103
auto dock_stack_2 = Gtk::make_managed<gPanthera::DockStack>(layout_manager, "Two", "two");
104
auto switcher_2 = Gtk::make_managed<gPanthera::DockStackSwitcher>(dock_stack_2, Gtk::Orientation::VERTICAL);
105
auto pane_1_content = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0);
106
auto debug_button = Gtk::make_managed<Gtk::Button>("Debug");
107
pane_1_content->append(*debug_button);
108
auto pane_2_content = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0);
109
auto test_notebook = Gtk::make_managed<Gtk::Notebook>();
110
test_notebook->append_page(*Gtk::make_managed<Gtk::Label>("Test 1"), *Gtk::make_managed<Gtk::Label>("Test 1"));
111
pane_2_content->append(*test_notebook);
112
auto pane_3_content = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0);
113
pane_3_content->append(*Gtk::make_managed<Gtk::Label>("Pane 3 content"));
114
auto pane_1_icon = Gtk::make_managed<Gtk::Image>();
115
pane_1_icon->set_from_icon_name("go-home-symbolic");
116
auto pane_2_icon = Gtk::make_managed<Gtk::Image>();
117
pane_2_icon->set_from_icon_name("folder-symbolic");
118
auto pane_3_icon = Gtk::make_managed<Gtk::Image>();
119
pane_3_icon->set_from_icon_name("network-transmit-receive-symbolic");
120
auto pane_1 = Gtk::make_managed<gPanthera::DockablePane>(layout_manager, *pane_1_content, "pane1", "Pane 1", pane_1_icon);
121
auto pane_2 = Gtk::make_managed<gPanthera::DockablePane>(layout_manager, *pane_2_content, "pane2", "Pane 2", pane_2_icon);
122
auto pane_3 = Gtk::make_managed<gPanthera::DockablePane>(layout_manager, *pane_3_content, "pane3", "Pane 3", pane_3_icon);
123
124
dock_stack_1->set_transition_type(Gtk::StackTransitionType::SLIDE_LEFT_RIGHT);
125
dock_stack_1->set_transition_duration(125);
126
dock_stack_1->set_expand(true);
127
dock_stack_2->set_transition_type(Gtk::StackTransitionType::SLIDE_UP_DOWN);
128
dock_stack_2->set_transition_duration(125);
129
dock_stack_2->set_expand(true);
130
131
auto outer_grid = Gtk::make_managed<Gtk::Grid>();
132
outer_grid->attach(*switcher_2, 0, 1, 1, 1);
133
outer_grid->attach(*switcher_1, 1, 2, 1, 1);
134
auto outer_paned = Gtk::make_managed<Gtk::Paned>(Gtk::Orientation::HORIZONTAL);
135
outer_paned->set_start_child(*dock_stack_2);
136
auto inner_paned = Gtk::make_managed<Gtk::Paned>(Gtk::Orientation::VERTICAL);
137
auto content = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0);
138
content_manager = std::make_shared<gPanthera::ContentManager>();
139
std::function<bool(gPanthera::ContentPage*)> detach_handler;
140
detach_handler = [](gPanthera::ContentPage *widget) {
141
auto new_stack = Gtk::make_managed<gPanthera::ContentStack>(widget->content_manager, widget->get_stack()->get_detach_handler());
142
auto new_switcher = Gtk::make_managed<gPanthera::ContentTabBar>(new_stack, Gtk::Orientation::HORIZONTAL, dynamic_cast<gPanthera::ContentTabBar*>(widget->get_stack()->get_parent()->get_first_child())->get_extra_child_function());
143
auto new_notebook = Gtk::make_managed<gPanthera::ContentNotebook>(new_stack, new_switcher);
144
auto window = new gPanthera::ContentWindow(new_notebook);
145
widget->redock(new_stack);
146
window->present();
147
new_stack->signal_leave_empty.connect([window]() {
148
window->close();
149
delete window;
150
});
151
return true;
152
};
153
154
auto return_extra_child = [this](gPanthera::ContentTabBar *switcher) {
155
auto new_tab_button = Gtk::make_managed<Gtk::Button>();
156
new_tab_button->set_child(*Gtk::make_managed<Gtk::Image>(Gio::Icon::create("list-add-symbolic")));
157
new_tab_button->set_tooltip_text("New tab");
158
new_tab_button->signal_clicked().connect([this, switcher]() {
159
on_new_tab(switcher->get_stack());
160
});
161
return new_tab_button;
162
};
163
auto content_stack = Gtk::make_managed<gPanthera::ContentStack>(content_manager, detach_handler);
164
auto content_stack_switcher = Gtk::make_managed<gPanthera::ContentTabBar>(content_stack, Gtk::Orientation::HORIZONTAL, return_extra_child);
165
content_manager->add_stack(content_stack);
166
WebKitWebView *webview = WEBKIT_WEB_VIEW(webkit_web_view_new()); // for some reason, this has to be created
167
content->set_name("content_box");
168
auto content_notebook = Gtk::make_managed<gPanthera::ContentNotebook>(content_stack, content_stack_switcher, Gtk::PositionType::TOP);
169
content->append(*content_notebook);
170
inner_paned->set_start_child(*content);
171
inner_paned->set_end_child(*dock_stack_1);
172
outer_paned->set_end_child(*inner_paned);
173
outer_grid->attach(*outer_paned, 1, 1, 1, 1);
174
auto main_toolbar = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
175
url_bar = Gtk::make_managed<Gtk::Entry>();
176
url_bar->set_placeholder_text("Enter URL");
177
url_bar->set_hexpand(true);
178
auto go_button = Gtk::make_managed<Gtk::Button>("Go");
179
auto back_button = Gtk::make_managed<Gtk::Button>();
180
back_button->set_child(*Gtk::make_managed<Gtk::Image>(Gio::Icon::create("go-previous-symbolic")));
181
back_button->set_tooltip_text("Back");
182
auto forward_button = Gtk::make_managed<Gtk::Button>();
183
forward_button->set_child(*Gtk::make_managed<Gtk::Image>(Gio::Icon::create("go-next-symbolic")));
184
forward_button->set_tooltip_text("Forward");
185
auto reload_button = Gtk::make_managed<Gtk::Button>();
186
reload_button->set_child(*Gtk::make_managed<Gtk::Image>(Gio::Icon::create("view-refresh-symbolic")));
187
reload_button->set_tooltip_text("Reload");
188
back_button->signal_clicked().connect([this]() {
189
auto page = content_manager->get_last_operated_page();
190
if(page) {
191
if(auto webview = WEBKIT_WEB_VIEW(page->get_child()->get_first_child()->gobj())) {
192
webkit_web_view_go_back(webview);
193
}
194
}
195
});
196
forward_button->signal_clicked().connect([this]() {
197
auto page = content_manager->get_last_operated_page();
198
if(page) {
199
if(auto webview = WEBKIT_WEB_VIEW(page->get_child()->get_first_child()->gobj())) {
200
webkit_web_view_go_forward(webview);
201
}
202
}
203
});
204
reload_button->signal_clicked().connect([this]() {
205
auto page = content_manager->get_last_operated_page();
206
if(page) {
207
if(auto webview = WEBKIT_WEB_VIEW(page->get_child()->get_first_child()->gobj())) {
208
webkit_web_view_reload(webview);
209
}
210
}
211
});
212
main_toolbar->append(*back_button);
213
main_toolbar->append(*forward_button);
214
main_toolbar->append(*reload_button);
215
main_toolbar->append(*url_bar);
216
main_toolbar->append(*go_button);
217
outer_grid->attach(*main_toolbar, 0, 0, 2, 1);
218
auto load_url_callback = [this]() {
219
auto page = content_manager->get_last_operated_page();
220
if(page) {
221
if(auto webview = WEBKIT_WEB_VIEW(page->get_child()->get_first_child()->gobj())) {
222
webkit_web_view_load_uri(webview, url_bar->get_text().c_str());
223
}
224
}
225
};
226
go_button->signal_clicked().connect(load_url_callback);
227
url_bar->signal_activate().connect(load_url_callback);
228
content_manager->signal_page_operated.connect([this](gPanthera::ContentPage *page) {
229
if(!page->get_child()) {
230
return;
231
}
232
if(!page->get_child()->get_first_child()) {
233
return;
234
}
235
url_bar->set_text(webkit_web_view_get_uri(WEBKIT_WEB_VIEW(page->get_child()->get_first_child()->gobj())));
236
guint url_update_handler = g_signal_connect(page->get_child()->get_first_child()->gobj(), "notify", G_CALLBACK(notify_focused_callback), this);
237
std::shared_ptr<sigc::connection> control_signal_handler = std::make_shared<sigc::connection>();
238
*control_signal_handler = page->signal_control_status_changed.connect([this, page, control_signal_handler, url_update_handler](bool controlled) {
239
if(!controlled) {
240
control_signal_handler->disconnect();
241
g_signal_handler_disconnect(page->get_child()->get_first_child()->gobj(), url_update_handler);
242
}
243
});
244
});
245
window->set_child(*outer_grid);
246
debug_button->signal_clicked().connect([this]() {
247
if(content_manager->get_last_operated_page()) {
248
std::cout << "Last operated page: " << content_manager->get_last_operated_page()->get_name() << std::endl;
249
} else {
250
std::cout << "No page operated!" << std::endl;
251
}
252
});
253
// TODO: Use the last operated page and allow opening tabs next to the last operated page using certain panes
254
// Load the existing layout, if it exists
255
std::ifstream layout_file_in("layout.json");
256
if(layout_file_in) {
257
std::string layout_json((std::istreambuf_iterator<char>(layout_file_in)), std::istreambuf_iterator<char>());
258
layout_file_in.close();
259
layout_manager->restore_json_layout(layout_json);
260
} else {
261
// Create a new layout if the file doesn't exist
262
layout_file_in.close();
263
264
dock_stack_1->add_pane(*pane_1);
265
dock_stack_1->add_pane(*pane_3);
266
dock_stack_2->add_pane(*pane_2);
267
268
std::ofstream layout_file_out("layout.json");
269
layout_file_out << layout_manager->get_layout_as_json();
270
layout_file_out.close();
271
}
272
// Save the layout when changed
273
layout_manager->signal_pane_moved.connect([this](gPanthera::DockablePane *pane) {
274
std::ofstream layout_file_out("layout.json");
275
layout_file_out << layout_manager->get_layout_as_json();
276
layout_file_out.close();
277
std::cout << "Layout changed: " << layout_manager->get_layout_as_json() << std::endl;
278
});
279
280
auto new_tab_action = Gio::SimpleAction::create("new_tab");
281
new_tab_action->signal_activate().connect([this](const Glib::VariantBase&) {
282
on_new_tab(nullptr);
283
});
284
add_action(new_tab_action);
285
set_accels_for_action("app.new_tab", {"<Primary>T"});
286
auto close_tab_action = Gio::SimpleAction::create("close_tab");
287
close_tab_action->signal_activate().connect([this](const Glib::VariantBase&) {
288
auto page = content_manager->get_last_operated_page();
289
if(page) {
290
page->close();
291
}
292
});
293
add_action(close_tab_action);
294
set_accels_for_action("app.close_tab", {"<Primary>W"});
295
}
296
297
void on_activate() override {
298
window->present();
299
}
300
public:
301
static Glib::RefPtr<PantheraWww> create() {
302
return Glib::make_refptr_for_instance<PantheraWww>(new PantheraWww());
303
}
304
};
305
306
int main(int argc, char *argv[]) {
307
gPanthera::init();
308
auto app = PantheraWww::create();
309
return app->run(argc, argv);
310
}
311