Skip to content

Commit 30aaf0a

Browse files
committed
chore: Split out PickerGroupBox to handle custom allocation behavior
1 parent 5f90491 commit 30aaf0a

File tree

2 files changed

+335
-263
lines changed

2 files changed

+335
-263
lines changed

src/picker/mod.rs

Lines changed: 16 additions & 263 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,21 @@
11
use cascade::cascade;
2-
use futures::{prelude::*, stream::FuturesUnordered};
3-
use glib::clone;
42
use gtk::prelude::*;
53
use gtk::subclass::prelude::*;
64
use once_cell::sync::Lazy;
7-
use std::{cell::RefCell, collections::HashMap, rc::Rc};
5+
use std::collections::HashMap;
86

97
use crate::Keyboard;
108
use backend::DerefCell;
119

1210
mod picker_group;
11+
mod picker_group_box;
1312
mod picker_json;
1413
mod picker_key;
1514

16-
use picker_group::PickerGroup;
15+
use picker_group_box::PickerGroupBox;
1716
use picker_json::picker_json;
1817
use picker_key::PickerKey;
1918

20-
const DEFAULT_COLS: usize = 3;
21-
const HSPACING: i32 = 64;
22-
const VSPACING: i32 = 32;
23-
const PICKER_CSS: &str = r#"
24-
button {
25-
margin: 0;
26-
padding: 0;
27-
}
28-
29-
.selected {
30-
border-color: #fbb86c;
31-
border-width: 4px;
32-
}
33-
"#;
34-
3519
pub static SCANCODE_LABELS: Lazy<HashMap<String, String>> = Lazy::new(|| {
3620
let mut labels = HashMap::new();
3721
for group in picker_json() {
@@ -44,187 +28,41 @@ pub static SCANCODE_LABELS: Lazy<HashMap<String, String>> = Lazy::new(|| {
4428

4529
#[derive(Default)]
4630
pub struct PickerInner {
47-
groups: DerefCell<Vec<PickerGroup>>,
48-
keys: DerefCell<HashMap<String, Rc<PickerKey>>>,
49-
keyboard: RefCell<Option<Keyboard>>,
50-
selected: RefCell<Vec<String>>,
31+
group_box: DerefCell<PickerGroupBox>,
5132
}
5233

5334
#[glib::object_subclass]
5435
impl ObjectSubclass for PickerInner {
5536
const NAME: &'static str = "S76KeyboardPicker";
56-
type ParentType = gtk::Container;
37+
type ParentType = gtk::Box;
5738
type Type = Picker;
5839
}
5940

6041
impl ObjectImpl for PickerInner {
6142
fn constructed(&self, picker: &Picker) {
6243
self.parent_constructed(picker);
6344

64-
let style_provider = cascade! {
65-
gtk::CssProvider::new();
66-
..load_from_data(&PICKER_CSS.as_bytes()).expect("Failed to parse css");
67-
};
68-
69-
let mut groups = Vec::new();
70-
let mut keys = HashMap::new();
71-
72-
for json_group in picker_json() {
73-
let mut group = PickerGroup::new(json_group.label, json_group.cols);
74-
75-
for json_key in json_group.keys {
76-
let key = PickerKey::new(
77-
json_key.keysym.clone(),
78-
json_key.label,
79-
json_group.width,
80-
&style_provider,
81-
);
82-
83-
group.add_key(key.clone());
84-
keys.insert(json_key.keysym, key);
85-
}
86-
87-
groups.push(group);
88-
}
89-
90-
for group in &groups {
91-
group.vbox.show();
92-
group.vbox.set_parent(picker);
93-
}
94-
95-
self.keys.set(keys);
96-
self.groups.set(groups);
45+
let group_box = PickerGroupBox::new();
9746

9847
cascade! {
9948
picker;
100-
..connect_signals();
49+
..add(&group_box);
10150
..show_all();
10251
};
103-
}
104-
}
105-
106-
impl WidgetImpl for PickerInner {
107-
fn get_request_mode(&self, _widget: &Self::Type) -> gtk::SizeRequestMode {
108-
gtk::SizeRequestMode::HeightForWidth
109-
}
110-
111-
fn get_preferred_width(&self, _widget: &Self::Type) -> (i32, i32) {
112-
let minimum_width = self
113-
.groups
114-
.iter()
115-
.map(|x| x.vbox.get_preferred_width().1)
116-
.max()
117-
.unwrap();
118-
let natural_width = self
119-
.groups
120-
.chunks(3)
121-
.map(|row| {
122-
row.iter()
123-
.map(|x| x.vbox.get_preferred_width().1)
124-
.sum::<i32>()
125-
})
126-
.max()
127-
.unwrap()
128-
+ 2 * HSPACING;
129-
(minimum_width, natural_width)
130-
}
131-
132-
fn get_preferred_height_for_width(&self, widget: &Self::Type, width: i32) -> (i32, i32) {
133-
let rows = widget.rows_for_width(width);
134-
let height = rows
135-
.iter()
136-
.map(|row| {
137-
row.iter()
138-
.map(|x| x.vbox.get_preferred_height().1)
139-
.max()
140-
.unwrap()
141-
})
142-
.sum::<i32>()
143-
+ (rows.len() as i32 - 1) * VSPACING;
144-
145-
(height, height)
146-
}
147-
148-
fn size_allocate(&self, obj: &Self::Type, allocation: &gtk::Allocation) {
149-
self.parent_size_allocate(obj, allocation);
150-
151-
let rows = obj.rows_for_width(allocation.width);
152-
153-
let total_width = rows
154-
.iter()
155-
.map(|row| {
156-
row.iter()
157-
.map(|x| x.vbox.get_preferred_width().1)
158-
.sum::<i32>()
159-
+ (row.len() as i32 - 1) * HSPACING
160-
})
161-
.max()
162-
.unwrap();
163-
164-
let mut y = 0;
165-
for row in rows {
166-
let mut x = (allocation.width - total_width) / 2;
167-
for group in row {
168-
let height = group.vbox.get_preferred_height().1;
169-
let width = group.vbox.get_preferred_width().1;
170-
group.vbox.size_allocate(&gtk::Allocation {
171-
x,
172-
y,
173-
width,
174-
height,
175-
});
176-
x += width + HSPACING;
177-
}
178-
y += row
179-
.iter()
180-
.map(|x| x.vbox.get_preferred_height().1)
181-
.max()
182-
.unwrap()
183-
+ VSPACING;
184-
}
185-
}
186-
187-
fn realize(&self, widget: &Self::Type) {
188-
let allocation = widget.get_allocation();
189-
widget.set_realized(true);
190-
191-
let attrs = gdk::WindowAttr {
192-
x: Some(allocation.x),
193-
y: Some(allocation.y),
194-
width: allocation.width,
195-
height: allocation.height,
196-
window_type: gdk::WindowType::Child,
197-
event_mask: widget.get_events(),
198-
wclass: gdk::WindowWindowClass::InputOutput,
199-
..Default::default()
200-
};
20152

202-
let window = gdk::Window::new(widget.get_parent_window().as_ref(), &attrs);
203-
widget.register_window(&window);
204-
widget.set_window(&window);
53+
self.group_box.set(group_box);
20554
}
20655
}
20756

208-
impl ContainerImpl for PickerInner {
209-
fn forall(
210-
&self,
211-
_obj: &Self::Type,
212-
_include_internals: bool,
213-
cb: &gtk::subclass::container::Callback,
214-
) {
215-
for group in self.groups.iter() {
216-
cb.call(group.vbox.upcast_ref());
217-
}
218-
}
57+
impl BoxImpl for PickerInner {}
21958

220-
fn remove(&self, _obj: &Self::Type, child: &gtk::Widget) {
221-
child.unparent();
222-
}
223-
}
59+
impl WidgetImpl for PickerInner {}
60+
61+
impl ContainerImpl for PickerInner {}
22462

22563
glib::wrapper! {
22664
pub struct Picker(ObjectSubclass<PickerInner>)
227-
@extends gtk::Container, gtk::Widget, @implements gtk::Orientable;
65+
@extends gtk::Box, gtk::Container, gtk::Widget, @implements gtk::Orientable;
22866
}
22967

23068
impl Picker {
@@ -236,101 +74,16 @@ impl Picker {
23674
PickerInner::from_instance(self)
23775
}
23876

239-
fn connect_signals(&self) {
240-
let picker = self;
241-
for group in self.inner().groups.iter() {
242-
for key in group.iter_keys() {
243-
let button = &key.gtk;
244-
let name = key.name.to_string();
245-
button.connect_clicked(clone!(@weak picker => @default-panic, move |_| {
246-
let kb = match picker.inner().keyboard.borrow().clone() {
247-
Some(kb) => kb,
248-
None => {
249-
return;
250-
}
251-
};
252-
let layer = kb.layer();
253-
254-
info!("Clicked {} layer {:?}", name, layer);
255-
if let Some(layer) = layer {
256-
let futures = FuturesUnordered::new();
257-
for i in kb.selected().iter() {
258-
let i = *i;
259-
futures.push(clone!(@strong kb, @strong name => async move {
260-
kb.keymap_set(i, layer, &name).await;
261-
}));
262-
}
263-
glib::MainContext::default().spawn_local(async {futures.collect::<()>().await});
264-
}
265-
}));
266-
}
267-
}
268-
}
269-
270-
fn get_button(&self, scancode_name: &str) -> Option<&gtk::Button> {
271-
self.inner().keys.get(scancode_name).map(|k| &k.gtk)
272-
}
273-
27477
pub(crate) fn set_keyboard(&self, keyboard: Option<Keyboard>) {
275-
if let Some(old_kb) = &*self.inner().keyboard.borrow() {
276-
old_kb.set_picker(None);
277-
}
78+
self.inner().group_box.set_keyboard(keyboard.clone());
79+
27880
if let Some(kb) = &keyboard {
279-
for group in self.inner().groups.iter() {
280-
for key in group.iter_keys() {
281-
// Check that scancode is available for the keyboard
282-
let visible = kb.has_scancode(&key.name);
283-
key.gtk.set_visible(visible);
284-
}
285-
}
28681
kb.set_picker(Some(&self));
28782
}
288-
*self.inner().keyboard.borrow_mut() = keyboard;
28983
}
29084

29185
pub(crate) fn set_selected(&self, scancode_names: Vec<String>) {
292-
let mut selected = self.inner().selected.borrow_mut();
293-
294-
for i in selected.iter() {
295-
if let Some(button) = self.get_button(i) {
296-
button.get_style_context().remove_class("selected");
297-
}
298-
}
299-
300-
*selected = scancode_names;
301-
302-
for i in selected.iter() {
303-
if let Some(button) = self.get_button(i) {
304-
button.get_style_context().add_class("selected");
305-
}
306-
}
307-
}
308-
309-
fn rows_for_width(&self, container_width: i32) -> Vec<&[PickerGroup]> {
310-
let mut rows = Vec::new();
311-
let groups = &*self.inner().groups;
312-
313-
let mut row_start = 0;
314-
let mut row_width = 0;
315-
for (i, group) in groups.iter().enumerate() {
316-
let width = group.vbox.get_preferred_width().1;
317-
318-
row_width += width;
319-
if i != 0 {
320-
row_width += HSPACING;
321-
}
322-
if i - row_start >= DEFAULT_COLS || row_width > container_width {
323-
rows.push(&groups[row_start..i]);
324-
row_start = i;
325-
row_width = width;
326-
}
327-
}
328-
329-
if !groups[row_start..].is_empty() {
330-
rows.push(&groups[row_start..]);
331-
}
332-
333-
rows
86+
self.inner().group_box.set_selected(scancode_names);
33487
}
33588
}
33689

0 commit comments

Comments
 (0)