From b9f1424b5492781c8f678b86791e63aeb0d8afc2 Mon Sep 17 00:00:00 2001 From: Ethan Simmons Date: Sun, 28 Apr 2024 17:57:40 -0500 Subject: [PATCH] Add drawing functionality to new ui --- src/wayland.rs | 113 ++++++++++++++++--- src/wayland/ui.rs | 279 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 311 insertions(+), 81 deletions(-) diff --git a/src/wayland.rs b/src/wayland.rs index b67eb1c..3b7fa2e 100644 --- a/src/wayland.rs +++ b/src/wayland.rs @@ -5,7 +5,10 @@ use smithay_client_toolkit::{ output::{OutputHandler, OutputState}, registry::{RegistryState, ProvidesRegistryState}, registry_handlers, - seat::{SeatState, SeatHandler, Capability}, + seat::{ + pointer::{PointerEvent, PointerEventKind, PointerHandler}, + SeatState, SeatHandler, Capability + }, shell::{ xdg::{ window::{WindowHandler, Window, WindowConfigure, WindowDecorations}, @@ -17,13 +20,14 @@ use smithay_client_toolkit::{ Shm, ShmHandler }, delegate_registry, delegate_compositor, delegate_seat, delegate_output, - delegate_xdg_shell, delegate_shm, delegate_activation, delegate_xdg_window + delegate_xdg_shell, delegate_shm, delegate_activation, delegate_xdg_window, + delegate_pointer, }; use wayland_client::{ Connection, QueueHandle, globals::registry_queue_init, - protocol::{wl_surface, wl_output, wl_seat, wl_shm}, + protocol::{wl_surface, wl_output, wl_seat, wl_shm, wl_pointer}, }; use std::time::Duration; @@ -31,10 +35,9 @@ use std::time::Duration; use anyhow::Result; mod ui; -mod pixel; mod app; -use ui::{ButtonBuilder, DrawAreaBuilder, TextAreaBuilder}; +use ui::{ButtonBuilder, DrawAreaBuilder, TextAreaBuilder, DrawPath, Position}; const WINDOW_HEIGHT: u32 = 256; @@ -83,15 +86,21 @@ pub(super) fn run() -> Result<()> { registry_state: RegistryState::new(&globals), seat_state: SeatState::new(&globals, &qh), output_state: OutputState::new(&globals, &qh), + xdg_activation, + shm, buffer: None, pool, window, width: WINDOW_WIDTH, height: WINDOW_HEIGHT, + cursor_down: false, exit: false, first_configure: true, + pointer: None, _loop_handle: event_loop.handle(), + + draw_path: DrawPath::new(), }; loop { @@ -112,14 +121,20 @@ struct SimpleWindow { seat_state: SeatState, output_state: OutputState, shm: Shm, + xdg_activation: Option, + buffer: Option, pool: SlotPool, window: Window, width: u32, height: u32, + cursor_down: bool, exit: bool, first_configure: bool, - _loop_handle: LoopHandle<'static, SimpleWindow> + pointer: Option, + _loop_handle: LoopHandle<'static, SimpleWindow>, + + draw_path: DrawPath, } impl CompositorHandler for SimpleWindow { @@ -142,14 +157,15 @@ impl CompositorHandler for SimpleWindow { _: &wl_surface::WlSurface, _: u32 ) { - self.draw(conn, qh); - } + self.draw(conn, qh); + } } impl SeatHandler for SimpleWindow { fn seat_state(&mut self) -> &mut SeatState { &mut self.seat_state } + fn new_seat( &mut self, _: &Connection, @@ -159,17 +175,28 @@ impl SeatHandler for SimpleWindow { fn new_capability( &mut self, _: &Connection, - _: &QueueHandle, - _: wl_seat::WlSeat, - _: Capability, - ) {} + qh: &QueueHandle, + seat: wl_seat::WlSeat, + capability: Capability, + ) { + if capability == Capability::Pointer && self.pointer.is_none() { + println!("Set pointer capability"); + let pointer = self.seat_state.get_pointer(qh, &seat).expect("Failed to create pointer"); + self.pointer = Some(pointer); + } + } fn remove_capability( &mut self, _: &Connection, _: &QueueHandle, _: wl_seat::WlSeat, - _: Capability, - ) {} + capability: Capability, + ) { + if capability == Capability::Pointer && self.pointer.is_some() { + println!("Unset pointer capability"); + self.pointer.take().unwrap().release(); + } + } fn remove_seat( &mut self, _: &Connection, @@ -236,7 +263,57 @@ impl ShmHandler for SimpleWindow { impl ActivationHandler for SimpleWindow { type RequestData = RequestData; - fn new_token(&mut self, _: String, _: &Self::RequestData) {} + fn new_token(&mut self, token: String, _: &Self::RequestData) { + self.xdg_activation + .as_ref() + .unwrap() + .activate::(self.window.wl_surface(), token); + } +} + +impl PointerHandler for SimpleWindow { + fn pointer_frame( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _pointer: &wl_pointer::WlPointer, + events: &[PointerEvent], + ) { + use PointerEventKind::*; + for event in events { + // Ignore events for other surfaces + if &event.surface != self.window.wl_surface() { + continue; + } + + match event.kind { + Enter { .. } => { + println!("Pointer entered @{:?}", event.position); + } + Leave { .. } => { + println!("Pointer left"); + } + Motion { .. } => { + if self.cursor_down { + self.draw_path.add_point(Position::new(event.position.0 as f32, event.position.1 as f32), false); + } + } + Press { button, .. } => { + println!("Press {:x} @ {:?}", button, event.position); + self.cursor_down = true; + self.draw_path.add_point(Position::new(event.position.0 as f32, event.position.1 as f32), true) + } + Release { button, .. } => { + println!("Release {:x} @ {:?}", button, event.position); + self.cursor_down = false; + self.draw_path.add_point(Position::new(event.position.0 as f32, event.position.1 as f32), false) + } + Axis { horizontal, vertical, .. } => { + println!("Scroll H:{horizontal:?}, V:{vertical:?}"); + } + } + } + } } impl SimpleWindow { @@ -280,13 +357,12 @@ impl SimpleWindow { button.position(self.width as f32 - 100.0, 50.0); button.width(100.0); button.height(50.0); - button.color(0x33, 0x33, 0x33); let button = button.finish(); draw_area.position(0.0, 50.0); draw_area.width(self.width as f32 - 100.0); draw_area.height(self.height as f32 - 50.0); - draw_area.color(0x15, 0x15, 0x15); + draw_area.path(&self.draw_path); let draw_area = draw_area.finish(); @@ -294,7 +370,6 @@ impl SimpleWindow { text_area.position(0.0, 0.0); text_area.width(self.width as f32); text_area.height(50.0); - text_area.color(0x22, 0x22, 0x22); let text_area = text_area.finish(); window.add_widget(button); @@ -322,6 +397,8 @@ delegate_xdg_shell!(SimpleWindow); delegate_xdg_window!(SimpleWindow); delegate_activation!(SimpleWindow); +delegate_pointer!(SimpleWindow); + delegate_registry!(SimpleWindow); impl ProvidesRegistryState for SimpleWindow { diff --git a/src/wayland/ui.rs b/src/wayland/ui.rs index 1a1b0e6..3d9a79e 100644 --- a/src/wayland/ui.rs +++ b/src/wayland/ui.rs @@ -1,4 +1,3 @@ -use crate::wayland::pixel::{Pixel, Box, BoxMut, PixelEncoding}; use raqote::{DrawTarget, PathBuilder, Source, StrokeStyle, DrawOptions, SolidSource, Color}; struct UIColor { @@ -72,11 +71,77 @@ impl<'a, 'b> Window<'a, 'b> { } } +#[derive(Clone, Copy, Debug)] pub(super) struct Position { pub(super) x: f32, pub(super) y: f32, } +pub(super) struct Path(Vec); + +impl IntoIterator for Path { + type Item = Position; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> IntoIterator for &'a Path { + type Item = &'a Position; + type IntoIter = std::slice::Iter<'a, Position>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +pub(super) struct DrawPath { + pub(super) paths: Vec, + pub(super) newline: bool, +} + +impl DrawPath { + pub(super) fn new() -> Self { + Self { + paths: Vec::new(), + newline: false, + } + } + + pub(super) fn add_point(&mut self, point: Position, newline: bool) { + if newline { + self.paths.push(Path(vec![point])) + } else if self.paths.len() > 0 { + self.paths + .last_mut() + .unwrap() + .0 + .push(point); + } + } +} + +impl IntoIterator for DrawPath { + type Item = Path; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.paths.into_iter() + } +} + +impl<'a> IntoIterator for &'a DrawPath { + type Item = &'a Path; + type IntoIter = std::slice::Iter<'a, Path>; + + fn into_iter(self) -> Self::IntoIter { + self.paths.iter() + } +} + + impl Position { pub(super) fn new(x: f32, y: f32) -> Self { Position { x, y } @@ -84,40 +149,60 @@ impl Position { } pub(super) enum Widget<'a> { - Button(Button<'a>), + Button(Button), DrawArea(DrawArea<'a>), - TextArea(TextArea<'a>) + TextArea(TextArea) } pub(super) trait Draw { fn render(&self, area: &mut DrawTarget<&mut [u32]>); } -pub(super) struct Button<'a> { +pub(super) struct ButtonColors { + background: Color, + border: Color, + glyph: Color, +} + +impl Default for ButtonColors { + fn default() -> Self { + Self { + background: Color::new(0xff, 0x25, 0x2c, 0x33), + border: Color::new(0xff, 0x77, 0x7f, 0x97), + glyph: Color::new(0xff, 0xea, 0xea, 0xe6), + } + } +} + +pub(super) struct Button { pub(super) width: f32, pub(super) height: f32, pub(super) position: Position, - pub(super) source: Source<'a>, + pub(super) colors: ButtonColors, pub(super) options: DrawOptions, } -pub(super) struct ButtonBuilder<'a> { +impl Button { + fn background_source(&self) -> Source { + Source::Solid(self.colors.background.into()) + } +} + +pub(super) struct ButtonBuilder { pub(super) width: Option, pub(super) height: Option, pub(super) position: Option, - pub(super) source: Option>, - pub(super) color: Option, + pub(super) colors: Option, pub(super) options: Option, } -impl<'a> ButtonBuilder<'a> { +impl<'a> ButtonBuilder { pub(super) fn new() -> Self { Self { width: None, height: None, position: None, - source: None, - color: None, + colors: None, options: None, } } @@ -134,12 +219,8 @@ impl<'a> ButtonBuilder<'a> { self.position = Some(Position::new(x, y)) } - pub(super) fn source(&mut self, source: Source<'a>) { - self.source = Some(source); - } - - pub(super) fn color(&mut self, r: u8, g: u8, b: u8) { - self.color = Some(Color::new(0xff, r, g, b)) + pub(super) fn colors(&mut self, colors: ButtonColors) { + self.colors = Some(colors) } pub(super) fn finish(self) -> Widget<'a> { @@ -148,18 +229,14 @@ impl<'a> ButtonBuilder<'a> { width: self.width.expect("Button must have width"), height: self.height.expect("Button must have height"), position: self.position.expect("Button must have a position"), - source: self.source.unwrap_or( - Source::Solid( - self.color.unwrap_or(Color::new(0xff, 0x00, 0x00, 0x00)).into() - ) - ), + colors: self.colors.unwrap_or_default(), options: self.options.unwrap_or(DrawOptions::new()), } ) } } -impl<'a> Draw for Button<'a> { +impl Draw for Button { fn render(&self, area: &mut DrawTarget<&mut [u32]>) { (*area).fill_rect( @@ -167,26 +244,58 @@ impl<'a> Draw for Button<'a> { self.position.y, self.width, self.height, - &self.source, + &self.background_source(), &self.options, ) } } +pub(super) struct DrawAreaColors { + background_color: Color, + drawing_color: Color, + border_color: Color, +} + +impl Default for DrawAreaColors { + fn default() -> Self { + Self { + background_color: Color::new(0xff, 0x4a, 0x4e, 0x64), + drawing_color: Color::new(0xff, 0xea, 0xea, 0xe6), + border_color: Color::new(0xff, 0x77, 0x7f, 0x97), + } + } +} + pub(super) struct DrawArea<'a> { pub(super) width: f32, pub(super) height: f32, pub(super) position: Position, - pub(super) source: Source<'a>, + pub(super) path: &'a DrawPath, + pub(super) colors: DrawAreaColors, pub(super) options: DrawOptions, } +impl DrawArea<'_> { + fn background_source(&self) -> Source { + Source::Solid(self.colors.background_color.into()) + } + + fn drawing_source(&self) -> Source { + Source::Solid(self.colors.drawing_color.into()) + } + + fn border_source(&self) -> Source { + Source::Solid(self.colors.border_color.into()) + } +} + + pub(super) struct DrawAreaBuilder<'a> { pub(super) width: Option, pub(super) height: Option, pub(super) position: Option, - pub(super) source: Option>, - pub(super) color: Option, + pub(super) path: Option<&'a DrawPath>, + pub(super) colors: Option, pub(super) options: Option, } @@ -196,8 +305,8 @@ impl<'a> DrawAreaBuilder<'a> { width: None, height: None, position: None, - source: None, - color: None, + path: None, + colors: None, options: None, } } @@ -214,12 +323,12 @@ impl<'a> DrawAreaBuilder<'a> { self.position = Some(Position::new(x, y)) } - pub(super) fn source(&mut self, source: Source<'a>) { - self.source = Some(source); + pub(super) fn colors(&mut self, colors: DrawAreaColors) { + self.colors = Some(colors) } - pub(super) fn color(&mut self, r: u8, g: u8, b: u8) { - self.color = Some(Color::new(0xff, r, g, b)) + pub(super) fn path(&mut self, path: &'a DrawPath) { + self.path = Some(path); } pub(super) fn finish(self) -> Widget<'a> { @@ -228,56 +337,104 @@ impl<'a> DrawAreaBuilder<'a> { width: self.width.expect("Draw area must have width"), height: self.height.expect("Draw area must have height"), position: self.position.expect("Draw area must have a position"), - source: self.source.unwrap_or( - Source::Solid( - self.color.unwrap_or(Color::new(0xff, 0x00, 0x00, 0x00)).into() - ) - ), + path: self.path.expect("Draw must have a path"), + colors: self.colors.unwrap_or_default(), options: self.options.unwrap_or(DrawOptions::new()), } ) } } -impl<'a> Draw for DrawArea<'a> { +impl Draw for DrawArea<'_> { fn render(&self, area: &mut DrawTarget<&mut [u32]>) { + let mut draw_path = PathBuilder::new(); + + for path in self.path.into_iter() { + if path.0.len() > 1 { + let mut path = path.into_iter(); + + let first_point = path + .by_ref() + .next(); + + draw_path.move_to(first_point.unwrap().x, first_point.unwrap().y); + + for point in path { + draw_path.line_to(point.x, point.y); + } + + } + + } + + + let path = draw_path.finish(); area.fill_rect( self.position.x, self.position.y, self.width, self.height, - &self.source, + &self.background_source(), &self.options, - ) + ); + + area.stroke( + &path, + &self.drawing_source(), + &StrokeStyle {width: 3.0, ..StrokeStyle::default()}, + &self.options, + ); } } -pub(super) struct TextArea<'a> { +pub(super) struct TextAreaColors { + pub(super) background: Color, + pub(super) text: Color, +} + +impl Default for TextAreaColors { + fn default() -> Self { + Self { + background: Color::new(0xff, 0x25, 0x2c, 0x33), + text: Color::new(0xff, 0xfc, 0xf8, 0xef), + } + } +} + +pub(super) struct TextArea { pub(super) width: f32, pub(super) height: f32, pub(super) position: Position, - pub(super) source: Source<'a>, + pub(super) colors: TextAreaColors, pub(super) options: DrawOptions, } -pub(super) struct TextAreaBuilder<'a> { +impl TextArea { + pub(super) fn text_source(&self) -> Source { + Source::Solid(self.colors.text.into()) + } + + pub(super) fn background_source(&self) -> Source { + Source::Solid(self.colors.background.into()) + } +} + +pub(super) struct TextAreaBuilder { pub(super) width: Option, pub(super) height: Option, pub(super) position: Option, - pub(super) source: Option>, - pub(super) color: Option, + pub(super) colors: Option, pub(super) options: Option, } -impl<'a> TextAreaBuilder<'a> { +impl<'a> TextAreaBuilder { pub(super) fn new() -> Self { Self { width: None, height: None, position: None, - source: None, - color: None, + colors: None, options: None, } } @@ -294,12 +451,9 @@ impl<'a> TextAreaBuilder<'a> { self.position = Some(Position::new(x, y)) } - pub(super) fn source(&mut self, source: Source<'a>) { - self.source = Some(source); - } - pub(super) fn color(&mut self, r: u8, g: u8, b: u8) { - self.color = Some(Color::new(0xff, r, g, b)) + pub(super) fn colors(&mut self, colors: TextAreaColors) { + self.colors = Some(colors); } pub(super) fn finish(self) -> Widget<'a> { @@ -308,28 +462,27 @@ impl<'a> TextAreaBuilder<'a> { width: self.width.expect("Button must have width"), height: self.height.expect("Button must have height"), position: self.position.expect("Button must have a position"), - source: self.source.unwrap_or( - Source::Solid( - self.color.unwrap_or(Color::new(0xff, 0x00, 0x00, 0x00)).into() - ) - ), + colors: self.colors.unwrap_or_default(), options: self.options.unwrap_or(DrawOptions::new()), } ) } } -impl<'a> Draw for TextArea<'a> { +impl Draw for TextArea { fn render(&self, area: &mut DrawTarget<&mut [u32]>) { + + - (*area).fill_rect( + area.fill_rect( self.position.x, self.position.y, self.width, self.height, - &self.source, + &self.background_source(), &self.options, - ) + ); + } }