Skip to content

Commit d45376b

Browse files
ids1024jackpot51
authored andcommitted
improv: Subclass gtk::Application
Having a struct here (whether or not it's a GObject) makes it possible to have methods for things like adding a keyboard.
1 parent d29b2f1 commit d45376b

File tree

1 file changed

+149
-83
lines changed

1 file changed

+149
-83
lines changed

src/application/mod.rs

Lines changed: 149 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
use cascade::cascade;
22
use gio::prelude::*;
3+
use glib::subclass;
4+
use glib::subclass::prelude::*;
35
use gtk::prelude::*;
6+
use gtk::subclass::prelude::*;
7+
use glib::translate::{FromGlibPtrFull, ToGlib, ToGlibPtr};
8+
use once_cell::unsync::OnceCell;
9+
use std::sync::atomic::{AtomicUsize, Ordering};
410
use std::rc::Rc;
511

612
use crate::daemon::{Daemon, DaemonClient, DaemonDummy, daemon_server};
@@ -15,97 +21,168 @@ mod rect;
1521
use keyboard::Keyboard;
1622
use picker::Picker;
1723

18-
fn main_app(app: &gtk::Application, daemon: Rc<dyn Daemon>) {
19-
let boards = daemon.boards().expect("Failed to load boards");
24+
pub struct ConfiguratorAppInner {
25+
board_dropdown: gtk::ComboBoxText,
26+
count: AtomicUsize,
27+
picker: Picker,
28+
scrolled_window: gtk::ScrolledWindow,
29+
stack: gtk::Stack,
30+
window: OnceCell<gtk::ApplicationWindow>,
31+
}
2032

21-
let board_dropdown = cascade! {
22-
gtk::ComboBoxText::new();
23-
};
33+
impl ObjectSubclass for ConfiguratorAppInner {
34+
const NAME: &'static str = "S76ConfiguratorApp";
2435

25-
let stack = cascade! {
26-
gtk::Stack::new();
27-
..set_transition_duration(0);
28-
};
36+
type ParentType = gtk::Application;
2937

30-
let picker = Picker::new();
38+
type Instance = subclass::simple::InstanceStruct<Self>;
39+
type Class = subclass::simple::ClassStruct<Self>;
3140

32-
board_dropdown.connect_changed(clone!(@weak stack, @weak picker => @default-panic, move |combobox| {
33-
if let Some(id) = combobox.get_active_id() {
34-
stack.set_visible_child_name(&id);
35-
let keyboard = stack.get_child_by_name(&id).unwrap().downcast().unwrap();
36-
picker.set_keyboard(Some(keyboard));
37-
}
38-
}));
41+
glib_object_subclass!();
3942

40-
let mut count = 0;
41-
for (i, board) in boards.iter().enumerate() {
42-
if let Some(keyboard) = Keyboard::new_board(board, daemon.clone(), i) {
43-
board_dropdown.append(Some(&board), &board);
44-
stack.add_named(&keyboard, &board);
45-
count += 1;
46-
47-
if count == 1 {
48-
keyboard.show();
49-
board_dropdown.set_active_id(Some(&board));
50-
picker.set_keyboard(Some(keyboard.clone()));
43+
fn new() -> Self {
44+
let board_dropdown = cascade! {
45+
gtk::ComboBoxText::new();
46+
};
47+
48+
let stack = cascade! {
49+
gtk::Stack::new();
50+
..set_transition_duration(0);
51+
};
52+
53+
let picker = Picker::new();
54+
55+
board_dropdown.connect_changed(clone!(@weak stack, @weak picker => @default-panic, move |combobox| {
56+
if let Some(id) = combobox.get_active_id() {
57+
stack.set_visible_child_name(&id);
58+
let keyboard = stack.get_child_by_name(&id).unwrap().downcast().unwrap();
59+
picker.set_keyboard(Some(keyboard));
5160
}
52-
} else {
53-
eprintln!("Failed to locate layout for '{}'", board);
61+
}));
62+
63+
let vbox = cascade! {
64+
gtk::Box::new(gtk::Orientation::Vertical, 32);
65+
..set_property_margin(10);
66+
..set_halign(gtk::Align::Center);
67+
..add(&board_dropdown);
68+
..add(&stack);
69+
..add(&picker);
70+
};
71+
72+
let scrolled_window = cascade! {
73+
gtk::ScrolledWindow::new::<gtk::Adjustment, gtk::Adjustment>(None, None);
74+
..add(&vbox);
75+
};
76+
77+
Self {
78+
board_dropdown,
79+
count: AtomicUsize::new(0),
80+
picker,
81+
scrolled_window,
82+
stack,
83+
window: OnceCell::new(),
5484
}
5585
}
86+
}
87+
88+
impl ObjectImpl for ConfiguratorAppInner {
89+
glib_object_impl!();
90+
91+
fn constructed(&self, obj: &glib::Object) {
92+
self.parent_constructed(obj);
93+
94+
let app: &ConfiguratorApp = obj.downcast_ref().unwrap();
95+
app.set_application_id(Some("com.system76.keyboard-layout"));
96+
}
97+
}
5698

57-
if count == 0 {
58-
eprintln!("Failed to locate any keyboards, showing demo");
99+
impl ApplicationImpl for ConfiguratorAppInner {
100+
fn activate(&self, app: &gio::Application) {
101+
let app: &ConfiguratorApp = app.downcast_ref().unwrap();
59102

60-
let board_names = layout::layouts().iter().map(|s| s.to_string()).collect();
61-
let daemon = Rc::new(DaemonDummy::new(board_names));
62-
let boards = daemon.boards().unwrap();
103+
if let Some(window) = app.get_active_window() {
104+
//TODO
105+
eprintln!("Focusing current window");
106+
window.present();
107+
} else {
108+
let window = cascade! {
109+
gtk::ApplicationWindow::new(app);
110+
..set_title("Keyboard Layout");
111+
..set_position(gtk::WindowPosition::Center);
112+
..set_default_size(1024, 768);
113+
..add(&app.inner().scrolled_window);
114+
};
115+
116+
window.set_focus::<gtk::Widget>(None);
117+
window.show_all();
118+
119+
window.connect_destroy(|_| {
120+
eprintln!("Window close");
121+
});
63122

64-
for (i, board) in boards.iter().enumerate() {
65-
if let Some(keyboard) = Keyboard::new_board(board, daemon.clone(), i) {
66-
board_dropdown.append(Some(&board), &board);
67-
stack.add_named(&keyboard, &board);
68-
count += 1;
123+
let _ = app.inner().window.set(window);
124+
125+
let daemon = daemon();
126+
let boards = daemon.boards().expect("Failed to load boards");
127+
128+
for (i, board) in boards.iter().enumerate() {
129+
app.add_keyboard(daemon.clone(), board, i);
130+
}
69131

70-
if count == 1 {
71-
keyboard.show();
72-
board_dropdown.set_active_id(Some(&board));
73-
picker.set_keyboard(Some(keyboard.clone()));
132+
if app.inner().count.load(Ordering::Relaxed) == 0 {
133+
eprintln!("Failed to locate any keyboards, showing demo");
134+
135+
let board_names = layout::layouts().iter().map(|s| s.to_string()).collect();
136+
let daemon = Rc::new(DaemonDummy::new(board_names));
137+
let boards = daemon.boards().unwrap();
138+
139+
for (i, board) in boards.iter().enumerate() {
140+
app.add_keyboard(daemon.clone(), board, i);
74141
}
75-
} else {
76-
eprintln!("Failed to locate layout for '{}'", board);
77142
}
78143
}
79144
}
145+
}
80146

81-
let vbox = cascade! {
82-
gtk::Box::new(gtk::Orientation::Vertical, 32);
83-
..set_property_margin(10);
84-
..set_halign(gtk::Align::Center);
85-
..add(&board_dropdown);
86-
..add(&stack);
87-
..add(&picker);
88-
};
147+
impl GtkApplicationImpl for ConfiguratorAppInner {}
89148

90-
let scrolled_window = cascade! {
91-
gtk::ScrolledWindow::new::<gtk::Adjustment, gtk::Adjustment>(None, None);
92-
..add(&vbox);
93-
};
149+
glib_wrapper! {
150+
pub struct ConfiguratorApp(
151+
Object<subclass::simple::InstanceStruct<ConfiguratorAppInner>,
152+
subclass::simple::ClassStruct<ConfiguratorAppInner>, ConfiguratorAppClass>)
153+
@extends gtk::Application, gio::Application;
94154

95-
let window = cascade! {
96-
gtk::ApplicationWindow::new(app);
97-
..set_title("Keyboard Layout");
98-
..set_position(gtk::WindowPosition::Center);
99-
..set_default_size(1024, 768);
100-
..add(&scrolled_window);
101-
};
155+
match fn {
156+
get_type => || ConfiguratorAppInner::get_type().to_glib(),
157+
}
158+
}
159+
160+
impl ConfiguratorApp {
161+
fn new() -> Self {
162+
glib::Object::new(Self::static_type(), &[])
163+
.unwrap()
164+
.downcast()
165+
.unwrap()
166+
}
102167

103-
window.set_focus::<gtk::Widget>(None);
104-
window.show_all();
168+
fn inner(&self) -> &ConfiguratorAppInner {
169+
ConfiguratorAppInner::from_instance(self)
170+
}
171+
172+
fn add_keyboard(&self, daemon: Rc<dyn Daemon>, board: &str, i: usize) {
173+
if let Some(keyboard) = Keyboard::new_board(board, daemon.clone(), i) {
174+
keyboard.show_all();
175+
self.inner().board_dropdown.append(Some(&board), &board);
176+
self.inner().stack.add_named(&keyboard, &board);
105177

106-
window.connect_destroy(|_| {
107-
eprintln!("Window close");
108-
});
178+
if self.inner().count.fetch_add(1, Ordering::Relaxed) == 0 {
179+
self.inner().board_dropdown.set_active_id(Some(&board));
180+
self.inner().picker.set_keyboard(Some(keyboard.clone()));
181+
}
182+
} else {
183+
eprintln!("Failed to locate layout for '{}'", board);
184+
}
185+
}
109186
}
110187

111188
#[cfg(target_os = "linux")]
@@ -190,19 +267,8 @@ pub fn run(args: Vec<String>) -> i32 {
190267
#[cfg(target_os = "windows")]
191268
windows_init();
192269

193-
let application =
194-
gtk::Application::new(Some("com.system76.keyboard-layout"), Default::default())
195-
.expect("Failed to create gtk::Application");
196-
197-
application.connect_activate(move |app| {
198-
if let Some(window) = app.get_active_window() {
199-
//TODO
200-
eprintln!("Focusing current window");
201-
window.present();
202-
} else {
203-
main_app(app, daemon());
204-
}
205-
});
270+
gtk::init().unwrap();
206271

272+
let application = ConfiguratorApp::new();
207273
application.run(&args)
208274
}

0 commit comments

Comments
 (0)