Skip to content

Commit b942d72

Browse files
authored
Futher Nuon work (#281)
* nuon: Fixup nuon scissor_rect inheritance * nuon: Remove scroll background * nuon: Label button widget * nuon: Settings widgets
1 parent 36e4334 commit b942d72

2 files changed

Lines changed: 250 additions & 30 deletions

File tree

nuon/src/lib.rs

Lines changed: 89 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ pub type Size = euclid::default::Size2D<f32>;
77
pub type Box2D = euclid::default::Box2D<f32>;
88
pub type Rect = euclid::default::Rect<f32>;
99

10+
mod settings;
11+
pub use settings::*;
12+
1013
#[derive(Default, Debug, Clone, Copy, PartialEq)]
1114
pub struct Color {
1215
pub r: f32,
@@ -334,7 +337,7 @@ impl Layer {
334337

335338
pub fn build(&self, ui: &mut Ui, build: impl FnOnce(&mut Ui)) {
336339
let rect = if self.rect == Rect::zero() {
337-
Rect::zero()
340+
ui.layers.current_mut().scissor_rect
338341
} else {
339342
Rect::new(
340343
ui.translation_stack.translate(self.rect.origin),
@@ -383,11 +386,6 @@ impl Scroll {
383386

384387
pub fn build(&self, ui: &mut Ui, build: impl FnOnce(&mut Ui)) {
385388
self::layer().scissor_rect(self.rect).build(ui, |ui| {
386-
self::quad()
387-
.size(self.rect.size.width, self.rect.size.height)
388-
.color([17; 3])
389-
.build(ui);
390-
391389
let last = self::translate().y(-self.scroll).build(ui, build);
392390
let last_y = last.y - self.rect.size.height;
393391

@@ -438,7 +436,7 @@ impl Card {
438436

439437
self::quad()
440438
.size(last.x, last.y)
441-
.color([42; 3])
439+
.color([37, 35, 42])
442440
.border_radius([5.0; 4])
443441
.build(ui);
444442

@@ -450,6 +448,38 @@ pub fn card() -> Card {
450448
Card::new()
451449
}
452450

451+
pub struct RowGroup {}
452+
453+
impl Default for RowGroup {
454+
fn default() -> Self {
455+
Self::new()
456+
}
457+
}
458+
459+
impl RowGroup {
460+
pub fn new() -> Self {
461+
Self {}
462+
}
463+
464+
pub fn build(&self, ui: &mut Ui, build: impl FnOnce(&mut Ui)) -> Point {
465+
let last = self::translate().build(ui, |ui| {
466+
self::layer().build(ui, build);
467+
});
468+
469+
self::quad()
470+
.size(last.x, last.y)
471+
.color([37, 35, 42])
472+
.border_radius([10.0; 4])
473+
.build(ui);
474+
475+
last
476+
}
477+
}
478+
479+
pub fn row_group() -> RowGroup {
480+
RowGroup::new()
481+
}
482+
453483
#[derive(Debug, Clone)]
454484
pub struct Quad {
455485
rect: Rect,
@@ -717,6 +747,7 @@ pub struct Button {
717747
preseed_color: Color,
718748
border_radius: [f32; 4],
719749
icon: &'static str,
750+
label: &'static str,
720751
text_justify: TextJustify,
721752
}
722753

@@ -741,6 +772,7 @@ impl Button {
741772
preseed_color: Color::new_u8(67, 65, 72, 1.0),
742773
border_radius: [0.0; 4],
743774
icon: "X",
775+
label: "",
744776
text_justify: TextJustify::Center,
745777
}
746778
}
@@ -805,6 +837,11 @@ impl Button {
805837
self
806838
}
807839

840+
pub fn label(mut self, label: &'static str) -> Self {
841+
self.label = label;
842+
self
843+
}
844+
808845
pub fn text_justify(mut self, text_justify: TextJustify) -> Self {
809846
self.text_justify = text_justify;
810847
self
@@ -813,6 +850,8 @@ impl Button {
813850
fn gen_id(&self) -> Id {
814851
if let Some(id) = self.id {
815852
Id::hash(id)
853+
} else if !self.label.is_empty() {
854+
Id::hash(self.label)
816855
} else {
817856
Id::hash(self.icon)
818857
}
@@ -843,31 +882,51 @@ impl Button {
843882
color,
844883
});
845884

846-
let icon_size = 20.0;
847-
let half_size = icon_size / 2.0;
848-
849-
let (x, y) = match self.text_justify {
850-
TextJustify::Left => {
851-
let y = rect.origin.y + rect.size.height / 2.0 - half_size;
852-
(rect.origin.x + 2.0, y)
853-
}
854-
TextJustify::Right => {
855-
let x = rect.origin.x + rect.size.width - icon_size;
856-
let y = rect.origin.y + rect.size.height / 2.0 - half_size;
857-
(x - 2.0, y)
858-
}
859-
TextJustify::Center => {
860-
let x = rect.origin.x + rect.size.width / 2.0 - half_size;
861-
let y = rect.origin.y + rect.size.height / 2.0 - half_size;
862-
(x, y)
863-
}
885+
let pad_x = match self.text_justify {
886+
TextJustify::Left => 1.0,
887+
TextJustify::Right => -1.0,
888+
TextJustify::Center => 0.0,
864889
};
865890

866-
layer.icons.push(IconRenderElement {
867-
origin: Point::new(x, y),
868-
size: icon_size,
869-
icon: self.icon.to_string(),
870-
});
891+
if self.label.is_empty() {
892+
let icon_size = 20.0;
893+
let half_size = icon_size / 2.0;
894+
let pad_x = pad_x * 2.0;
895+
896+
let (x, y) = match self.text_justify {
897+
TextJustify::Left => {
898+
let y = rect.origin.y + rect.size.height / 2.0 - half_size;
899+
(rect.origin.x + pad_x, y)
900+
}
901+
TextJustify::Right => {
902+
let x = rect.origin.x + rect.size.width - icon_size;
903+
let y = rect.origin.y + rect.size.height / 2.0 - half_size;
904+
(x + pad_x, y)
905+
}
906+
TextJustify::Center => {
907+
let x = rect.origin.x + rect.size.width / 2.0 - half_size;
908+
let y = rect.origin.y + rect.size.height / 2.0 - half_size;
909+
(x, y)
910+
}
911+
};
912+
913+
layer.icons.push(IconRenderElement {
914+
origin: Point::new(x, y),
915+
size: icon_size,
916+
icon: self.icon.to_string(),
917+
});
918+
} else {
919+
let pad_x = pad_x * 10.0;
920+
921+
layer.text.push(TextRenderElement {
922+
rect: Rect::new(Point::new(rect.origin.x + pad_x, rect.origin.y), rect.size),
923+
text_justify: self.text_justify,
924+
size: 16.0,
925+
bold: false,
926+
text: self.label.to_string(),
927+
color: Color::new_u8(255, 255, 255, 1.0),
928+
});
929+
}
871930

872931
clicked
873932
}

nuon/src/settings.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use crate::{self as nuon, TextJustify, Ui};
2+
3+
pub struct SettingsSection<'a> {
4+
label: String,
5+
width: f32,
6+
rows: Vec<SettingsRow<'a>>,
7+
}
8+
9+
impl<'a> SettingsSection<'a> {
10+
pub fn new(label: impl Into<String>) -> Self {
11+
Self {
12+
label: label.into(),
13+
width: 0.0,
14+
rows: Vec::new(),
15+
}
16+
}
17+
18+
pub fn width(mut self, width: f32) -> Self {
19+
self.width = width;
20+
self
21+
}
22+
23+
pub fn row(mut self, row: SettingsRow<'a>) -> Self {
24+
self.rows.push(row);
25+
self
26+
}
27+
28+
pub fn build(self, ui: &mut Ui) {
29+
let spacer_label_h = 43.0;
30+
31+
let spacer = |ui: &mut Ui| {
32+
nuon::quad()
33+
.width(self.width)
34+
.height(1.0)
35+
.color([0; 3])
36+
.build(ui);
37+
nuon::translate().y(1.0).add_to_current(ui);
38+
};
39+
40+
nuon::label()
41+
.text(self.label)
42+
.size(self.width, spacer_label_h)
43+
.font_size(14.6)
44+
.text_justify(TextJustify::Left)
45+
.bold(true)
46+
.build(ui);
47+
48+
nuon::translate().y(spacer_label_h).add_to_current(ui);
49+
50+
let pos = nuon::row_group().build(ui, |ui| {
51+
let len = self.rows.len();
52+
for (id, row) in self.rows.into_iter().enumerate() {
53+
row.width(self.width).build(ui);
54+
if id + 1 < len {
55+
spacer(ui);
56+
}
57+
}
58+
59+
nuon::translate().x(self.width).add_to_current(ui);
60+
});
61+
62+
nuon::translate().y(pos.y).add_to_current(ui);
63+
}
64+
}
65+
66+
pub fn settings_section<'a>(label: impl Into<String>) -> SettingsSection<'a> {
67+
SettingsSection::new(label)
68+
}
69+
70+
pub struct SettingsRow<'a> {
71+
title: String,
72+
subtitle: String,
73+
width: f32,
74+
#[allow(clippy::type_complexity)]
75+
body: Box<dyn FnOnce(&mut Ui, f32, f32) + 'a>,
76+
}
77+
78+
impl<'a> Default for SettingsRow<'a> {
79+
fn default() -> Self {
80+
Self::new()
81+
}
82+
}
83+
84+
impl<'a> SettingsRow<'a> {
85+
pub fn new() -> Self {
86+
Self {
87+
title: String::new(),
88+
subtitle: String::new(),
89+
width: 0.0,
90+
body: Box::new(|_, _, _| {}),
91+
}
92+
}
93+
94+
fn width(mut self, width: f32) -> Self {
95+
self.width = width;
96+
self
97+
}
98+
99+
pub fn title(mut self, label: impl Into<String>) -> Self {
100+
self.title = label.into();
101+
self
102+
}
103+
104+
pub fn subtitle(mut self, label: impl Into<String>) -> Self {
105+
self.subtitle = label.into();
106+
self
107+
}
108+
109+
pub fn body(mut self, build: impl FnOnce(&mut Ui, f32, f32) + 'a) -> Self {
110+
self.body = Box::new(build);
111+
self
112+
}
113+
114+
fn build(self, ui: &mut Ui) {
115+
let row_h = 54.0;
116+
let row_padding = 15.0;
117+
let row_inner_w = self.width - 2.0 * row_padding;
118+
119+
nuon::translate().x(row_padding).build(ui, |ui| {
120+
let title_h = 14.6;
121+
let subtitle_h = 12.2;
122+
123+
if self.subtitle.is_empty() {
124+
nuon::label()
125+
.text(self.title)
126+
.text_justify(nuon::TextJustify::Left)
127+
.font_size(title_h)
128+
.size(row_inner_w, row_h)
129+
.build(ui);
130+
} else {
131+
let gap = 8.0;
132+
let sum_h = title_h + subtitle_h + gap;
133+
let y = row_h / 2.0 - sum_h / 2.0;
134+
135+
nuon::label()
136+
.y(y)
137+
.text(self.title)
138+
.text_justify(nuon::TextJustify::Left)
139+
.font_size(title_h)
140+
.size(row_inner_w, title_h)
141+
.build(ui);
142+
nuon::label()
143+
.y(y + gap + title_h)
144+
.text(self.subtitle)
145+
.color([1.0, 1.0, 1.0, 0.5])
146+
.text_justify(nuon::TextJustify::Left)
147+
.font_size(subtitle_h)
148+
.y(row_h / 2.0)
149+
.size(row_inner_w, subtitle_h)
150+
.build(ui);
151+
}
152+
153+
(self.body)(ui, row_inner_w, row_h);
154+
});
155+
nuon::translate().y(row_h).add_to_current(ui);
156+
}
157+
}
158+
159+
pub fn settings_row<'a>() -> SettingsRow<'a> {
160+
SettingsRow::new()
161+
}

0 commit comments

Comments
 (0)