Skip to content

Commit f1197f2

Browse files
committed
Label the axes of the xy widget
1 parent 3fed093 commit f1197f2

File tree

8 files changed

+183
-25
lines changed

8 files changed

+183
-25
lines changed

caw/examples/live_example.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ fn main() {
99
.with_volume(volume.clone());
1010
volume.set(knob("volume").initial_value_01(0.5).build());
1111
let tempo_s = sv(knob("tempo s").build() * 0.5);
12-
let (cutoff_hz, res) = xy("lpf").build().unzip();
12+
let (cutoff_hz, res) = xy("lpf")
13+
.axis_label_x("cutoff_hz")
14+
.axis_label_y("resonance")
15+
.build()
16+
.unzip();
1317
let cutoff_hz = sv(cutoff_hz);
1418
let res = sv(res * 2.);
1519
let clock = sv(periodic_trig_s(tempo_s.clone())

midi-udp-widgets-app-lib/src/lib.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@ pub struct Xy {
297297
widget: Widget,
298298
controller_x: u8,
299299
controller_y: u8,
300+
axis_label_x: Option<String>,
301+
axis_label_y: Option<String>,
300302
sig: Sig<
301303
Zip<
302304
MidiController01<SigShared<MidiChannelUdp>>,
@@ -307,7 +309,11 @@ pub struct Xy {
307309
}
308310

309311
impl Xy {
310-
pub fn new(title: String) -> Sig<SigShared<Self>> {
312+
pub fn new(
313+
title: String,
314+
axis_label_x: Option<String>,
315+
axis_label_y: Option<String>,
316+
) -> Sig<SigShared<Self>> {
311317
let mut xys_by_title = XYS_BY_TITLE.lock().unwrap();
312318
if let Some(xy) = xys_by_title.get(&title) {
313319
xy.clone()
@@ -335,6 +341,8 @@ impl Xy {
335341
controller_y,
336342
sig,
337343
channel_index,
344+
axis_label_x,
345+
axis_label_y,
338346
};
339347
let child = match s.command().spawn() {
340348
Ok(child) => child,
@@ -352,7 +360,7 @@ impl Xy {
352360

353361
fn command(&self) -> Command {
354362
let mut command = Command::new(PROGRAM_NAME);
355-
let args = vec![
363+
let mut args = vec![
356364
format!(
357365
"--server={}",
358366
sig_server_local_socket_address().to_string()
@@ -363,6 +371,12 @@ impl Xy {
363371
format!("--controller-x={}", self.controller_x),
364372
format!("--controller-y={}", self.controller_y),
365373
];
374+
if let Some(axis_label_x) = self.axis_label_x.as_ref() {
375+
args.push(format!("--axis-label-x={}", axis_label_x));
376+
}
377+
if let Some(axis_label_y) = self.axis_label_y.as_ref() {
378+
args.push(format!("--axis-label-y={}", axis_label_y));
379+
}
366380
command.args(args);
367381
command
368382
}
@@ -389,6 +403,19 @@ mod xy_builder {
389403
#[build_ty = "Sig<SigShared<Xy>>"]
390404
pub struct Props {
391405
title: String,
406+
#[default = None]
407+
axis_label_x_: Option<String>,
408+
#[default = None]
409+
axis_label_y_: Option<String>,
410+
}
411+
}
412+
413+
impl Props {
414+
pub fn axis_label_x(self, axis_label_x: impl AsRef<str>) -> Self {
415+
self.axis_label_x_(Some(axis_label_x.as_ref().to_string()))
416+
}
417+
pub fn axis_label_y(self, axis_label_y: impl AsRef<str>) -> Self {
418+
self.axis_label_y_(Some(axis_label_y.as_ref().to_string()))
392419
}
393420
}
394421

midi-udp-widgets-app/src/main.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use caw_keyboard::{Note, note};
66
use caw_midi::MidiEvent;
77
use caw_midi_udp_client::*;
8-
use caw_widgets::{Button, ComputerKeyboard, Knob, Xy};
8+
use caw_widgets::{AxisLabels, Button, ComputerKeyboard, Knob, Xy};
99
use clap::{Parser, Subcommand};
1010

1111
#[derive(Subcommand)]
@@ -24,6 +24,10 @@ enum Command {
2424
controller_x: u8,
2525
#[arg(long, default_value_t = 1)]
2626
controller_y: u8,
27+
#[arg(long)]
28+
axis_label_x: Option<String>,
29+
#[arg(long)]
30+
axis_label_y: Option<String>,
2731
},
2832
ComputerKeyboard {
2933
#[arg(long, default_value_t = note::B_2)]
@@ -89,8 +93,14 @@ fn main() {
8993
Command::Xy {
9094
controller_x,
9195
controller_y,
96+
axis_label_x,
97+
axis_label_y,
9298
} => {
93-
let mut xy = Xy::new(cli.title.as_deref()).unwrap();
99+
let axis_labels = AxisLabels {
100+
x: axis_label_x,
101+
y: axis_label_y,
102+
};
103+
let mut xy = Xy::new(cli.title.as_deref(), axis_labels).unwrap();
94104
loop {
95105
xy.tick().unwrap();
96106
let (x, y) = xy.value_midi();

widgets/src/button.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::window::Window;
1+
use crate::window::{TitlePosition, Window};
22
use anyhow::anyhow;
33
use sdl2::{pixels::Color, rect::Rect};
44
use std::time::Instant;
@@ -65,7 +65,7 @@ impl Button {
6565
fn render(&mut self) -> anyhow::Result<()> {
6666
self.window.canvas.set_draw_color(Color::BLACK);
6767
self.window.canvas.clear();
68-
self.window.render_title()?;
68+
self.window.render_title(TitlePosition::CenterBottom)?;
6969
self.render_button()?;
7070
self.window.canvas.present();
7171
Ok(())

widgets/src/computer_keyboard.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::window::Window;
1+
use crate::window::{TitlePosition, Window};
22
use anyhow::anyhow;
33
use caw_computer_keyboard::Key;
44
use caw_keyboard::Note;
@@ -163,7 +163,7 @@ impl ComputerKeyboard {
163163
fn render(&mut self) -> anyhow::Result<()> {
164164
self.window.canvas.set_draw_color(Color::BLACK);
165165
self.window.canvas.clear();
166-
self.window.render_title()?;
166+
self.window.render_title(TitlePosition::CenterBottom)?;
167167
self.render_note()?;
168168
self.window.canvas.present();
169169
Ok(())

widgets/src/knob.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::window::Window;
1+
use crate::window::{TitlePosition, Window};
22
use anyhow::anyhow;
33
use caw_window_utils::persistent::PersistentData;
44
use line_2d::Coord;
@@ -176,7 +176,7 @@ impl Knob {
176176
fn render(&mut self) -> anyhow::Result<()> {
177177
self.window.canvas.set_draw_color(Color::BLACK);
178178
self.window.canvas.clear();
179-
self.window.render_title()?;
179+
self.window.render_title(TitlePosition::CenterBottom)?;
180180
self.render_value()?;
181181
self.render_knob()?;
182182
self.window.canvas.present();

widgets/src/window.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use caw_window_utils::{
66
use sdl2::{
77
EventPump,
88
event::{Event, WindowEvent},
9-
gfx::rotozoom::RotozoomSurface,
109
pixels::Color,
1110
rect::Rect,
1211
render::{Canvas, TextureCreator},
@@ -19,6 +18,11 @@ use std::{
1918

2019
const FRAME_DURATION: Duration = Duration::from_micros(1_000_000 / 60);
2120

21+
pub enum TitlePosition {
22+
Center,
23+
CenterBottom,
24+
}
25+
2226
pub struct Window {
2327
pub canvas: Canvas<SdlWindow>,
2428
pub event_pump: EventPump,
@@ -76,26 +80,38 @@ impl Window {
7680
}
7781
}
7882

79-
pub fn render_title(&mut self) -> anyhow::Result<()> {
83+
pub fn render_title(
84+
&mut self,
85+
position: TitlePosition,
86+
) -> anyhow::Result<()> {
8087
if let Some(title) = self.title.as_ref() {
8188
let text_surface = self
8289
.font
8390
.render(title.as_str())
8491
.blended(Color::WHITE)
8592
.map_err(|e| anyhow!("{e}"))?;
86-
let text_surface_rot = text_surface.rotate_90deg(1).unwrap();
8793
let text_texture =
88-
text_surface_rot.as_texture(&self.texture_creator)?;
94+
text_surface.as_texture(&self.texture_creator)?;
8995
let (canvas_width, canvas_height) =
9096
self.canvas.output_size().map_err(|e| anyhow!("{e}"))?;
9197
let text_texture_query = text_texture.query();
9298
let value_space_px = 20;
93-
// Render the title centred at the bottom of the window.
99+
let x_position =
100+
(canvas_width as i32 - text_texture_query.width as i32) / 2;
101+
let y_position = match position {
102+
TitlePosition::Center => {
103+
(canvas_height as i32 - text_texture_query.height as i32)
104+
/ 2
105+
}
106+
TitlePosition::CenterBottom => {
107+
canvas_height as i32
108+
- text_texture_query.height as i32
109+
- value_space_px
110+
}
111+
};
94112
let text_rect = Rect::new(
95-
(canvas_width as i32 - text_texture_query.width as i32) / 2,
96-
canvas_height as i32
97-
- text_texture_query.height as i32
98-
- value_space_px,
113+
x_position,
114+
y_position,
99115
text_texture_query.width,
100116
text_texture_query.height,
101117
);

widgets/src/xy.rs

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
use crate::window::Window;
1+
use crate::window::{TitlePosition, Window};
22
use anyhow::anyhow;
33
use caw_window_utils::persistent::PersistentData;
44
use midly::num::u7;
5-
use sdl2::{mouse::MouseButton, pixels::Color, rect::Rect};
5+
use sdl2::{
6+
gfx::rotozoom::RotozoomSurface, mouse::MouseButton, pixels::Color,
7+
rect::Rect,
8+
};
69
use serde::{Deserialize, Serialize};
710
use std::time::Instant;
811

@@ -19,13 +22,23 @@ impl PersistentData for State {
1922
const NAME: &'static str = "xy_state";
2023
}
2124

25+
#[derive(Debug)]
26+
pub struct AxisLabels {
27+
pub x: Option<String>,
28+
pub y: Option<String>,
29+
}
30+
2231
pub struct Xy {
2332
window: Window,
2433
state: State,
34+
axis_labels: AxisLabels,
2535
}
2636

2737
impl Xy {
28-
pub fn new(title: Option<&str>) -> anyhow::Result<Self> {
38+
pub fn new(
39+
title: Option<&str>,
40+
axis_labels: AxisLabels,
41+
) -> anyhow::Result<Self> {
2942
let window = Window::new(title, WIDTH_PX, HEIGHT_PX)?;
3043
let state = if let Some(state) = title.and_then(|t| State::load_(t)) {
3144
state
@@ -35,7 +48,12 @@ impl Xy {
3548
y_px: HEIGHT_PX / 2,
3649
}
3750
};
38-
Ok(Self { window, state })
51+
println!("{:?}", axis_labels);
52+
Ok(Self {
53+
window,
54+
state,
55+
axis_labels,
56+
})
3957
}
4058

4159
fn handle_events(&mut self) {
@@ -92,11 +110,94 @@ impl Xy {
92110
Ok(())
93111
}
94112

113+
fn render_axes(
114+
&mut self,
115+
x: Option<&str>,
116+
y: Option<&str>,
117+
ortho_padding_px: i32,
118+
) -> anyhow::Result<()> {
119+
let padding_px = 10;
120+
if let Some(x) = x {
121+
let text_surface = self
122+
.window
123+
.font
124+
.render(x)
125+
.blended(Color::WHITE)
126+
.map_err(|e| anyhow!("{e}"))?;
127+
let text_texture =
128+
text_surface.as_texture(&self.window.texture_creator)?;
129+
let (canvas_width, canvas_height) = self
130+
.window
131+
.canvas
132+
.output_size()
133+
.map_err(|e| anyhow!("{e}"))?;
134+
let text_texture_query = text_texture.query();
135+
let x_position = canvas_width as i32
136+
- text_texture_query.width as i32
137+
- padding_px;
138+
let y_position = canvas_height as i32
139+
- text_texture_query.height as i32
140+
- ortho_padding_px;
141+
let text_rect = Rect::new(
142+
x_position,
143+
y_position,
144+
text_texture_query.width,
145+
text_texture_query.height,
146+
);
147+
self.window
148+
.canvas
149+
.copy(&text_texture, None, Some(text_rect))
150+
.map_err(|e| anyhow!("{e}"))?;
151+
}
152+
if let Some(y) = y {
153+
let text_surface = self
154+
.window
155+
.font
156+
.render(y)
157+
.blended(Color::WHITE)
158+
.map_err(|e| anyhow!("{e}"))?;
159+
let text_surface =
160+
text_surface.rotate_90deg(1).map_err(|s| anyhow!("{}", s))?;
161+
let text_texture =
162+
text_surface.as_texture(&self.window.texture_creator)?;
163+
let text_texture_query = text_texture.query();
164+
let x_position = ortho_padding_px;
165+
let y_position = padding_px;
166+
let text_rect = Rect::new(
167+
x_position,
168+
y_position,
169+
text_texture_query.width,
170+
text_texture_query.height,
171+
);
172+
self.window
173+
.canvas
174+
.copy(&text_texture, None, Some(text_rect))
175+
.map_err(|e| anyhow!("{e}"))?;
176+
}
177+
178+
Ok(())
179+
}
180+
181+
fn render_axes_labels(&mut self) -> anyhow::Result<()> {
182+
let x = self.axis_labels.x.as_ref().map(|x| format!("{}", x));
183+
let y = self.axis_labels.y.as_ref().map(|y| format!("{}", y));
184+
self.render_axes(x.as_deref(), y.as_deref(), 20)
185+
}
186+
187+
fn render_axes_values(&mut self) -> anyhow::Result<()> {
188+
let (x, y) = self.value_01();
189+
let x = format!("{}", x);
190+
let y = format!("{}", y);
191+
self.render_axes(Some(x.as_str()), Some(y.as_str()), 0)
192+
}
193+
95194
fn render(&mut self) -> anyhow::Result<()> {
96195
self.window.canvas.set_draw_color(Color::BLACK);
97196
self.window.canvas.clear();
98197
self.render_xy()?;
99-
self.window.render_title()?;
198+
self.window.render_title(TitlePosition::Center)?;
199+
self.render_axes_labels()?;
200+
self.render_axes_values()?;
100201
self.window.canvas.present();
101202
Ok(())
102203
}

0 commit comments

Comments
 (0)