Split into more modules and add bounds checking to widgets

This commit is contained in:
2024-05-05 04:35:28 -05:00
parent b9f1424b54
commit 1acaabb1c2
6 changed files with 824 additions and 906 deletions

View File

@@ -1,162 +1,12 @@
use relm4::prelude::{gtk, ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent};
use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, WidgetExt, GestureSingleExt};
use gtk::cairo::{Context, Operator};
use relm4::drawing::DrawHandler;
use std::error::Error;
use anyhow::Result;
use anyhow;
mod wayland;
mod process_point;
use process_point::{Point, print_words};
use wayland::app::SimpleWindow;
#[derive(Debug)]
enum AppInput {
Input,
NewLine((f64, f64)),
AddPoint((f64, f64)),
Reset,
}
fn main() -> anyhow::Result<()> {
#[derive(Debug)]
struct AppModel {
counter: u8,
points: Vec<Point>,
handler: DrawHandler,
}
#[derive(Debug)]
struct UpdatePoints {}
#[relm4::component]
impl SimpleComponent for AppModel {
type Init = u8;
type Input = AppInput;
type Output = UpdatePoints;
view! {
gtk::Window {
set_title: Some("Waywrite"),
set_default_width: 600,
set_default_height: 200,
gtk::Box{
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 5,
set_margin_all: 2,
set_hexpand: true,
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 5,
set_margin_all: 5,
set_vexpand: true,
#[local_ref]
area -> gtk::DrawingArea {
set_vexpand: true,
set_hexpand: true,
add_controller = gtk::GestureStylus {
set_button: 0,
connect_motion[sender] => move |_, x, y| {
sender.input(AppInput::AddPoint((x, y)));
},
connect_down[sender] => move |_, x, y| {
sender.input(AppInput::NewLine((x, y)));
}
}
},
},
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 4,
set_margin_all: 2,
set_halign: gtk::Align::End,
gtk::Button {
set_label: "Enter",
connect_clicked[sender] => move |_| {
sender.input(AppInput::Input);
}
},
gtk::Button {
set_label: "Erase",
connect_clicked[sender] => move |_| {
sender.input(AppInput::Reset);
}
},
}
}
}
}
fn init(
_: Self::Init,
root: &Self::Root,
sender: ComponentSender<Self>,
) ->relm4::ComponentParts<Self> {
let model = AppModel {
counter: 0,
points: Vec::new(),
handler: DrawHandler::new(),
};
let area = model.handler.drawing_area();
let widgets = view_output!();
ComponentParts { model, widgets }
}
fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
let cx = self.handler.get_context();
match message {
AppInput::Input => {
print_words(&self.points).unwrap();
}
AppInput::AddPoint((x, y)) => {
self.points.push(Point { x, y, new_line: false })
}
AppInput::NewLine((x, y)) => {
self.points.push(Point { x, y, new_line: true })
}
AppInput::Reset => {
cx.set_operator(Operator::Clear);
cx.set_source_rgba(0.0, 0.0, 0.0, 0.0);
cx.paint().expect("Could not fill context");
self.points = Vec::new();
}
}
draw(&cx, &self.points);
}
}
fn draw(cx: &Context, points: &[Point]) {
for (i, point) in points.into_iter().enumerate().filter(|(i, _)| *i != 0) {
if !point.new_line {
let last_point = &points[i - 1];
cx.move_to(last_point.x as f64, last_point.y as f64);
cx.line_to(point.x as f64, point.y as f64);
cx.set_source_rgb(200.0, 200.0, 200.0);
cx.set_line_width(2.0);
cx.stroke().expect("Failed to draw line");
}
}
}
fn main() -> Result<(), Box<dyn Error>> {
wayland::run()?;
SimpleWindow::run()?;
Ok(())
}

View File

@@ -1,409 +1,18 @@
use smithay_client_toolkit::reexports::calloop::{EventLoop, LoopHandle}; use smithay_client_toolkit::reexports::calloop_wayland_source::WaylandSource;
use smithay_client_toolkit::{
activation::{ActivationState, ActivationHandler, RequestData},
compositor::{CompositorState, CompositorHandler},
output::{OutputHandler, OutputState},
registry::{RegistryState, ProvidesRegistryState},
registry_handlers,
seat::{
pointer::{PointerEvent, PointerEventKind, PointerHandler},
SeatState, SeatHandler, Capability
},
shell::{
xdg::{
window::{WindowHandler, Window, WindowConfigure, WindowDecorations},
XdgShell,
},
WaylandSurface,
},
shm::{ slot::{SlotPool, Buffer},
Shm, ShmHandler
},
delegate_registry, delegate_compositor, delegate_seat, delegate_output,
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, wl_pointer},
};
use std::time::Duration;
use anyhow::Result;
mod ui;
mod app;
pub mod app;
mod draw;
mod builder;
use ui::{ButtonBuilder, DrawAreaBuilder, TextAreaBuilder, DrawPath, Position};
const WINDOW_HEIGHT: u32 = 256;
const WINDOW_WIDTH: u32 = 512;
pub(super) fn run() -> Result<()> {
let conn = Connection::connect_to_env()?;
let (globals, event_queue) = registry_queue_init(&conn)?;
let qh = event_queue.handle();
let mut event_loop: EventLoop::<SimpleWindow> =
EventLoop::try_new()?;
let loop_handle = event_loop.handle();
WaylandSource::new(conn.clone(), event_queue).insert(loop_handle)?;
let compositor = CompositorState::bind(&globals, &qh)?;
let xdg_shell = XdgShell::bind(&globals, &qh)?;
let shm = Shm::bind(&globals, &qh)?;
let xdg_activation = ActivationState::bind(&globals, &qh).ok();
let surface = compositor.create_surface(&qh);
let window = xdg_shell.create_window(surface, WindowDecorations::RequestServer, &qh);
window.set_title("A window");
window.set_app_id("simmer.simplewindow");
window.set_min_size(Some((WINDOW_WIDTH, WINDOW_HEIGHT)));
window.set_max_size(Some((WINDOW_WIDTH, WINDOW_HEIGHT)));
window.commit();
if let Some(activation) = xdg_activation.as_ref() {
activation.request_token(
&qh,
RequestData {
seat_and_serial: None,
surface: Some(window.wl_surface().clone()),
app_id: Some(String::from("simmer.waywrite")),
},
)
}
let pool = SlotPool::new((WINDOW_WIDTH as usize) * (WINDOW_HEIGHT as usize) * 4, &shm)?;
let mut simple_window = SimpleWindow {
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 {
event_loop.dispatch(Duration::from_millis(16), &mut simple_window)?;
if simple_window.exit {
println!("exiting");
break;
}
}
Ok(())
#[derive(Clone, Copy, Debug)]
pub(super) struct Position {
pub(super) x: f32,
pub(super) y: f32,
}
struct SimpleWindow {
registry_state: RegistryState,
seat_state: SeatState,
output_state: OutputState,
shm: Shm,
xdg_activation: Option<ActivationState>,
buffer: Option<Buffer>,
pool: SlotPool,
window: Window,
width: u32,
height: u32,
cursor_down: bool,
exit: bool,
first_configure: bool,
pointer: Option<wl_pointer::WlPointer>,
_loop_handle: LoopHandle<'static, SimpleWindow>,
draw_path: DrawPath,
}
impl CompositorHandler for SimpleWindow {
fn scale_factor_changed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &wl_surface::WlSurface,
_: i32) {}
fn transform_changed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &wl_surface::WlSurface,
_: wl_output::Transform) {}
fn frame(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
_: &wl_surface::WlSurface,
_: u32
) {
self.draw(conn, qh);
impl Position {
pub(super) fn new(x: f32, y: f32) -> Self {
Position { x, y }
}
}
impl SeatHandler for SimpleWindow {
fn seat_state(&mut self) -> &mut SeatState {
&mut self.seat_state
}
fn new_seat(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: wl_seat::WlSeat
) {}
fn new_capability(
&mut self,
_: &Connection,
qh: &QueueHandle<Self>,
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<Self>,
_: wl_seat::WlSeat,
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,
_: &QueueHandle<Self>,
_: wl_seat::WlSeat,
) {}
}
impl OutputHandler for SimpleWindow {
fn output_state(&mut self) -> &mut OutputState {
&mut self.output_state
}
fn new_output(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: wl_output::WlOutput,
) {}
fn update_output(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: wl_output::WlOutput,
) {}
fn output_destroyed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: wl_output::WlOutput,
) {}
}
impl WindowHandler for SimpleWindow {
fn request_close(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &Window
) {
self.exit = true;
}
fn configure(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
_: &Window,
_: WindowConfigure,
_: u32,
) {
if self.first_configure {
self.first_configure = false;
self.draw(conn, qh);
}
}
}
impl ShmHandler for SimpleWindow {
fn shm_state(&mut self) -> &mut Shm {
&mut self.shm
}
}
impl ActivationHandler for SimpleWindow {
type RequestData = RequestData;
fn new_token(&mut self, token: String, _: &Self::RequestData) {
self.xdg_activation
.as_ref()
.unwrap()
.activate::<SimpleWindow>(self.window.wl_surface(), token);
}
}
impl PointerHandler for SimpleWindow {
fn pointer_frame(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_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 {
fn draw(&mut self, _: &Connection, qh: &QueueHandle<Self>) {
let buffer = self.buffer.get_or_insert_with(|| {
self.pool
.create_buffer(self.width as i32, self.height as i32, (self.width as i32) * 4, wl_shm::Format::Argb8888)
.expect("create buffer")
.0
});
let canvas = match self.pool.canvas(buffer) {
Some(canvas) => canvas,
None => {
let (second_buffer, canvas) = self
.pool
.create_buffer(
self.width as i32,
self.height as i32,
(self.width as i32) * 4,
wl_shm::Format::Argb8888,
)
.expect("create buffer");
*buffer = second_buffer;
canvas
}
};
let draw_buffer: &mut [u32] = unsafe {canvas.align_to_mut::<u32>().1};
let mut window = ui::Window::new(
draw_buffer,
self.width.try_into().unwrap(),
self.height.try_into().unwrap()
);
let mut button = ButtonBuilder::new();
let mut draw_area = DrawAreaBuilder::new();
button.position(self.width as f32 - 100.0, 50.0);
button.width(100.0);
button.height(50.0);
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.path(&self.draw_path);
let draw_area = draw_area.finish();
let mut text_area = TextAreaBuilder::new();
text_area.position(0.0, 0.0);
text_area.width(self.width as f32);
text_area.height(50.0);
let text_area = text_area.finish();
window.add_widget(button);
window.add_widget(draw_area);
window.add_widget(text_area);
window.render();
self.window.wl_surface().damage_buffer(0, 0, self.width as i32, self.height as i32);
self.window.wl_surface().frame(qh, self.window.wl_surface().clone());
buffer.attach_to(self.window.wl_surface()).expect("buffer attach");
self.window.commit();
}
}
delegate_compositor!(SimpleWindow);
delegate_output!(SimpleWindow);
delegate_shm!(SimpleWindow);
delegate_seat!(SimpleWindow);
delegate_xdg_shell!(SimpleWindow);
delegate_xdg_window!(SimpleWindow);
delegate_activation!(SimpleWindow);
delegate_pointer!(SimpleWindow);
delegate_registry!(SimpleWindow);
impl ProvidesRegistryState for SimpleWindow {
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}
registry_handlers![OutputState, SeatState,];
}

View File

@@ -0,0 +1,446 @@
use smithay_client_toolkit::reexports::calloop::{EventLoop, LoopHandle}; use smithay_client_toolkit::reexports::calloop_wayland_source::WaylandSource;
use smithay_client_toolkit::{
activation::{ActivationState, ActivationHandler, RequestData},
compositor::{CompositorState, CompositorHandler},
output::{OutputHandler, OutputState},
registry::{RegistryState, ProvidesRegistryState},
registry_handlers,
seat::{
pointer::{PointerEvent, PointerEventKind, PointerHandler},
SeatState, SeatHandler, Capability
},
shell::{
xdg::{
window::{WindowHandler, Window, WindowConfigure, WindowDecorations},
XdgShell,
},
WaylandSurface,
},
shm::{ slot::{SlotPool, Buffer},
Shm, ShmHandler
},
delegate_registry, delegate_compositor, delegate_seat, delegate_output,
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, wl_pointer},
};
use std::time::Duration;
use anyhow::Result;
use crate::wayland::{
draw::DrawPath,
builder::{TextAreaBuilder, DrawAreaBuilder, ButtonBuilder},
ui::{self, Widget},
Position,
};
const WINDOW_HEIGHT: u32 = 256;
const WINDOW_WIDTH: u32 = 512;
pub(crate) struct SimpleWindow {
registry_state: RegistryState,
seat_state: SeatState,
output_state: OutputState,
shm: Shm,
xdg_activation: Option<ActivationState>,
buffer: Option<Buffer>,
pool: SlotPool,
window: Window,
ui: Option<ui::Window>,
width: u32,
height: u32,
cursor_down: bool,
exit: bool,
first_configure: bool,
pointer: Option<wl_pointer::WlPointer>,
_loop_handle: LoopHandle<'static, SimpleWindow>,
draw_path: DrawPath,
}
impl SimpleWindow {
pub(crate) fn run() -> Result<()> {
let conn = Connection::connect_to_env()?;
let (globals, event_queue) = registry_queue_init(&conn)?;
let qh = event_queue.handle();
let mut event_loop: EventLoop::<SimpleWindow> =
EventLoop::try_new()?;
let loop_handle = event_loop.handle();
WaylandSource::new(conn.clone(), event_queue).insert(loop_handle)?;
let compositor = CompositorState::bind(&globals, &qh)?;
let xdg_shell = XdgShell::bind(&globals, &qh)?;
let shm = Shm::bind(&globals, &qh)?;
let xdg_activation = ActivationState::bind(&globals, &qh).ok();
let surface = compositor.create_surface(&qh);
let window = xdg_shell.create_window(surface, WindowDecorations::RequestServer, &qh);
window.set_title("A window");
window.set_app_id("simmer.simplewindow");
window.set_min_size(Some((WINDOW_WIDTH, WINDOW_HEIGHT)));
window.set_max_size(Some((WINDOW_WIDTH, WINDOW_HEIGHT)));
window.commit();
if let Some(activation) = xdg_activation.as_ref() {
activation.request_token(
&qh,
RequestData {
seat_and_serial: None,
surface: Some(window.wl_surface().clone()),
app_id: Some(String::from("simmer.waywrite")),
},
)
}
let pool = SlotPool::new((WINDOW_WIDTH as usize) * (WINDOW_HEIGHT as usize) * 4, &shm)?;
let mut simple_window = SimpleWindow {
registry_state: RegistryState::new(&globals),
seat_state: SeatState::new(&globals, &qh),
output_state: OutputState::new(&globals, &qh),
xdg_activation,
shm,
buffer: None,
pool,
window,
ui: None,
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 {
event_loop.dispatch(Duration::from_millis(16), &mut simple_window)?;
if simple_window.exit {
println!("exiting");
break;
}
}
Ok(())
}
}
impl CompositorHandler for SimpleWindow {
fn scale_factor_changed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &wl_surface::WlSurface,
_: i32) {}
fn transform_changed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &wl_surface::WlSurface,
_: wl_output::Transform) {}
fn frame(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
_: &wl_surface::WlSurface,
_: u32
) {
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,
_: &QueueHandle<Self>,
_: wl_seat::WlSeat
) {}
fn new_capability(
&mut self,
_: &Connection,
qh: &QueueHandle<Self>,
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<Self>,
_: wl_seat::WlSeat,
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,
_: &QueueHandle<Self>,
_: wl_seat::WlSeat,
) {}
}
impl OutputHandler for SimpleWindow {
fn output_state(&mut self) -> &mut OutputState {
&mut self.output_state
}
fn new_output(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: wl_output::WlOutput,
) {}
fn update_output(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: wl_output::WlOutput,
) {}
fn output_destroyed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: wl_output::WlOutput,
) {}
}
impl WindowHandler for SimpleWindow {
fn request_close(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &Window
) {
self.exit = true;
}
fn configure(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
_: &Window,
_: WindowConfigure,
_: u32,
) {
if self.first_configure {
self.first_configure = false;
self.draw(conn, qh);
}
if self.ui.is_none() {
let mut draw_area = DrawAreaBuilder::new();
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.path(&mut self.draw_path);
let draw_area = draw_area.finish();
let mut window = ui::Window::new(
self.width.try_into().unwrap(),
self.height.try_into().unwrap(),
draw_area,
);
let mut button = ButtonBuilder::new();
button.position(self.width as f32 - 100.0, 50.0);
button.width(100.0);
button.height(50.0);
let button = button.finish();
let mut text_area = TextAreaBuilder::new();
text_area.position(0.0, 0.0);
text_area.width(self.width as f32);
text_area.height(50.0);
let text_area = text_area.finish();
window.add_widget(button);
window.add_widget(text_area);
self.ui = Some(window);
}
}
}
impl ShmHandler for SimpleWindow {
fn shm_state(&mut self) -> &mut Shm {
&mut self.shm
}
}
impl ActivationHandler for SimpleWindow {
type RequestData = RequestData;
fn new_token(&mut self, token: String, _: &Self::RequestData) {
self.xdg_activation
.as_ref()
.unwrap()
.activate::<SimpleWindow>(self.window.wl_surface(), token);
}
}
impl PointerHandler for SimpleWindow {
fn pointer_frame(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_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 {
if let Some(window) = &mut self.ui {
let pos = Position::new(event.position.0 as f32, event.position.1 as f32);
if window.draw_area.contains_point(pos) {
window.draw_area.path.add_point(pos, false);
}
}
}
}
Press { button, .. } => {
println!("Press {:x} @ {:?}", button, event.position);
self.cursor_down = true;
if let Some(window) = &mut self.ui {
let pos = Position::new(event.position.0 as f32, event.position.1 as f32);
if window.draw_area.contains_point(pos) {
window.draw_area.path.add_point(pos, true);
}
for widget in &window.widgets {
if let Widget::Button(button) = widget {
if button.contains_point(pos) {
window.draw_area.path = DrawPath::default();
}
}
}
}
}
Release { button, .. } => {
println!("Release {:x} @ {:?}", button, event.position);
self.cursor_down = false;
if let Some(window) = &mut self.ui {
let pos = Position::new(event.position.0 as f32, event.position.1 as f32);
if window.draw_area.contains_point(pos) {
window.draw_area.path.add_point(pos, false);
} else {
println!("Does not contain point")
}
}
}
Axis { horizontal, vertical, .. } => {
println!("Scroll H:{horizontal:?}, V:{vertical:?}");
}
}
}
}
}
impl SimpleWindow {
fn draw(&mut self, _: &Connection, qh: &QueueHandle<Self>) {
let buffer = self.buffer.get_or_insert_with(|| {
self.pool
.create_buffer(self.width as i32, self.height as i32, (self.width as i32) * 4, wl_shm::Format::Argb8888)
.expect("create buffer")
.0
});
let canvas = match self.pool.canvas(buffer) {
Some(canvas) => canvas,
None => {
let (second_buffer, canvas) = self
.pool
.create_buffer(
self.width as i32,
self.height as i32,
(self.width as i32) * 4,
wl_shm::Format::Argb8888,
)
.expect("create buffer");
*buffer = second_buffer;
canvas
}
};
if let Some(window) = &mut self.ui {
window.render(canvas);
}
self.window.wl_surface().damage_buffer(0, 0, self.width as i32, self.height as i32);
self.window.wl_surface().frame(qh, self.window.wl_surface().clone());
buffer.attach_to(self.window.wl_surface()).expect("buffer attach");
self.window.commit();
}
}
delegate_compositor!(SimpleWindow);
delegate_output!(SimpleWindow);
delegate_shm!(SimpleWindow);
delegate_seat!(SimpleWindow);
delegate_xdg_shell!(SimpleWindow);
delegate_xdg_window!(SimpleWindow);
delegate_activation!(SimpleWindow);
delegate_pointer!(SimpleWindow);
delegate_registry!(SimpleWindow);
impl ProvidesRegistryState for SimpleWindow {
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}
registry_handlers![OutputState, SeatState,];
}

187
src/wayland/builder.rs Normal file
View File

@@ -0,0 +1,187 @@
use crate::wayland::{
ui::{TextArea, TextAreaColors, Button, ButtonColors, Widget},
draw::{DrawAreaColors, DrawArea, DrawPath},
Position
};
use raqote::{DrawOptions, PathBuilder};
pub(super) struct TextAreaBuilder {
pub(super) width: Option<f32>,
pub(super) height: Option<f32>,
pub(super) position: Option<Position>,
pub(super) colors: Option<TextAreaColors>,
pub(super) options: Option<DrawOptions>,
}
impl<'a> TextAreaBuilder {
pub(super) fn new() -> Self {
Self {
width: None,
height: None,
position: None,
colors: None,
options: None,
}
}
pub(super) fn width(&mut self, width: f32) {
self.width = Some(width)
}
pub(super) fn height(&mut self, height: f32) {
self.height = Some(height)
}
pub(super) fn position(&mut self, x: f32, y: f32) {
self.position = Some(Position::new(x, y))
}
pub(super) fn colors(&mut self, colors: TextAreaColors) {
self.colors = Some(colors);
}
pub(super) fn finish(self) -> Widget {
Widget::TextArea(
TextArea {
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"),
colors: self.colors.unwrap_or_default(),
options: self.options.unwrap_or(DrawOptions::new()),
}
)
}
}
pub(super) struct ButtonBuilder {
pub(super) width: Option<f32>,
pub(super) height: Option<f32>,
pub(super) position: Option<Position>,
pub(super) colors: Option<ButtonColors>,
pub(super) options: Option<DrawOptions>,
}
impl ButtonBuilder {
pub(super) fn new() -> Self {
Self {
width: None,
height: None,
position: None,
colors: None,
options: None,
}
}
pub(super) fn width(&mut self, width: f32) {
self.width = Some(width)
}
pub(super) fn height(&mut self, height: f32) {
self.height = Some(height)
}
pub(super) fn position(&mut self, x: f32, y: f32) {
self.position = Some(Position::new(x, y))
}
pub(super) fn colors(&mut self, colors: ButtonColors) {
self.colors = Some(colors)
}
pub(super) fn finish(self) -> Widget {
let width = self.width.expect("Button must have a width");
let height = self.height.expect("Button must have a height");
let position = self.position.expect("Button must have a position");
let mut button_path = PathBuilder::new();
button_path.move_to(position.x, position.y);
button_path.line_to(position.x + width, position.y);
button_path.line_to(position.x + width, position.y + height);
button_path.line_to(position.x, position.y + height);
button_path.line_to(position.x, position.y);
let button_path = button_path.finish();
Widget::Button(
Button {
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"),
shape: button_path,
colors: self.colors.unwrap_or_default(),
options: self.options.unwrap_or(DrawOptions::new()),
}
)
}
}
pub(super) struct DrawAreaBuilder {
pub(super) width: Option<f32>,
pub(super) height: Option<f32>,
pub(super) position: Option<Position>,
pub(super) path: Option<DrawPath>,
pub(super) colors: Option<DrawAreaColors>,
pub(super) options: Option<DrawOptions>,
}
impl DrawAreaBuilder {
pub(super) fn new() -> Self {
Self {
width: None,
height: None,
position: None,
path: None,
colors: None,
options: None,
}
}
pub(super) fn width(&mut self, width: f32) {
self.width = Some(width)
}
pub(super) fn height(&mut self, height: f32) {
self.height = Some(height)
}
pub(super) fn position(&mut self, x: f32, y: f32) {
self.position = Some(Position::new(x, y))
}
pub(super) fn colors(&mut self, colors: DrawAreaColors) {
self.colors = Some(colors)
}
pub(super) fn path(&mut self, path: &mut DrawPath) {
self.path = Some(DrawPath {
paths: std::mem::take(&mut path.paths),
newline: path.newline,
});
}
pub(super) fn finish(self) -> DrawArea {
let width = self.width.expect("Button must have a width");
let height = self.height.expect("Button must have a height");
let position = self.position.expect("Button must have a position");
let mut draw_rect_path = PathBuilder::new();
draw_rect_path.move_to(position.x, position.y);
draw_rect_path.line_to(position.x + width, position.y);
draw_rect_path.line_to(position.x + width, position.y + height);
draw_rect_path.line_to(position.x, position.y + height);
draw_rect_path.line_to(position.x, position.y);
let draw_rect_path = draw_rect_path.finish();
DrawArea {
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"),
path: self.path.expect("Draw must have a path"),
shape: draw_rect_path,
colors: self.colors.unwrap_or_default(),
options: self.options.unwrap_or(DrawOptions::new()),
}
}
}

132
src/wayland/draw.rs Normal file
View File

@@ -0,0 +1,132 @@
use raqote::{DrawTarget, Color, StrokeStyle, PathBuilder, Source, DrawOptions};
use crate::wayland::Position;
pub(super) trait Draw {
fn render(&self, area: &mut DrawTarget<&mut [u32]>);
}
type Path = Vec<Position>;
#[derive(Default)]
pub(super) struct DrawPath {
pub(super) paths: Vec<Path>,
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(vec![point])
} else if self.paths.len() > 0 {
self.paths
.last_mut()
.unwrap()
.push(point);
}
}
pub(super) fn into_iter(self) -> std::vec::IntoIter<Path> {
self.paths.into_iter()
}
pub(super) fn iter(&self) -> std::slice::Iter<Path> {
self.paths.iter()
}
pub(super) fn iter_mut(&mut self) -> std::slice::IterMut<Path> {
self.paths.iter_mut()
}
}
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 {
pub(super) width: f32,
pub(super) height: f32,
pub(super) position: Position,
pub(super) path: DrawPath,
pub(super) shape: raqote::Path,
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(crate) fn contains_point(&self, point: Position) -> bool {
self.shape.contains_point(0.1, point.x, point.y)
}
}
impl Draw for DrawArea {
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
let mut draw_path = PathBuilder::new();
for path in self.path.iter() {
if path.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(
&self.shape,
&self.background_source(),
&self.options,
);
area.stroke(
&path,
&self.drawing_source(),
&StrokeStyle {width: 3.0, ..StrokeStyle::default()},
&self.options,
);
}
}

View File

@@ -1,6 +1,11 @@
use raqote::{DrawTarget, PathBuilder, Source, StrokeStyle, DrawOptions, SolidSource, Color};
use raqote::{DrawTarget, Source, DrawOptions, Color};
struct UIColor {
use crate::wayland::{
draw::{Draw, DrawArea},
Position,
};
pub(super) struct UIColors {
draw_area: Color,
button: Color,
button_accent: Color,
@@ -10,7 +15,7 @@ struct UIColor {
_text: Color,
}
impl Default for UIColor {
impl Default for UIColors {
fn default() -> Self {
Self {
draw_area: Color::new(0xff, 0x15, 0x15, 0x15),
@@ -24,35 +29,44 @@ impl Default for UIColor {
}
}
pub(super) struct Window<'a, 'b> {
pub(super) draw: DrawTarget<&'a mut [u32]>,
pub(super) options: DrawOptions,
pub(super) colors: UIColor,
pub(super) widgets: Vec<Widget<'b>>,
pub(super) width: usize,
pub(super) height: usize,
pub(super) enum Widget {
Button(Button),
TextArea(TextArea)
}
impl<'a, 'b> Window<'a, 'b> {
pub(super) fn new(buffer: &'a mut [u32], width: usize, height: usize) -> Self {
pub(super) struct Window {
pub(super) width: usize,
pub(super) height: usize,
pub(super) widgets: Vec<Widget>,
pub(super) draw_area: DrawArea,
pub(super) colors: UIColors,
pub(super) options: DrawOptions,
}
impl Window {
pub(super) fn new(width: usize, height: usize, draw_area: DrawArea) -> Self {
Self {
draw: DrawTarget::from_backing(width.try_into().unwrap(), height.try_into().unwrap(), buffer),
options: DrawOptions::new(),
colors: UIColor::default(),
widgets: Vec::new(),
width,
height,
widgets: Vec::new(),
draw_area,
colors: UIColors::default(),
options: DrawOptions::new(),
}
}
pub(super) fn add_widget(&mut self, widget: Widget<'b>) {
pub(super) fn add_widget(&mut self, widget: Widget) {
self.widgets.push(widget);
}
pub(super) fn render(&mut self) {
pub(super) fn render(&self, canvas: &mut [u8]) {
self.draw.fill_rect(
// TODO: Make sure this is actually safe
let draw_buffer: &mut [u32] = unsafe {canvas.align_to_mut::<u32>().1};
let mut target = DrawTarget::from_backing(self.width as i32, self.height as i32, draw_buffer);
target.fill_rect(
0.0,
0.0,
self.width as f32,
@@ -61,102 +75,17 @@ impl<'a, 'b> Window<'a, 'b> {
&self.options
);
self.draw_area.render(&mut target);
for widget in self.widgets.iter() {
match widget {
Widget::Button(button) => button.render(&mut self.draw),
Widget::DrawArea(draw_area) => draw_area.render(&mut self.draw),
Widget::TextArea(text_area) => text_area.render(&mut self.draw),
Widget::Button(button) => button.render(&mut target),
Widget::TextArea(text_area) => text_area.render(&mut target),
}
}
}
}
#[derive(Clone, Copy, Debug)]
pub(super) struct Position {
pub(super) x: f32,
pub(super) y: f32,
}
pub(super) struct Path(Vec<Position>);
impl IntoIterator for Path {
type Item = Position;
type IntoIter = std::vec::IntoIter<Position>;
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<Path>,
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<Path>;
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 }
}
}
pub(super) enum Widget<'a> {
Button(Button),
DrawArea(DrawArea<'a>),
TextArea(TextArea)
}
pub(super) trait Draw {
fn render(&self, area: &mut DrawTarget<&mut [u32]>);
}
pub(super) struct ButtonColors {
background: Color,
@@ -178,6 +107,7 @@ pub(super) struct Button {
pub(super) width: f32,
pub(super) height: f32,
pub(super) position: Position,
pub(super) shape: raqote::Path,
pub(super) colors: ButtonColors,
pub(super) options: DrawOptions,
}
@@ -186,208 +116,24 @@ impl Button {
fn background_source(&self) -> Source {
Source::Solid(self.colors.background.into())
}
pub(crate) fn contains_point(&self, point: Position) -> bool {
self.shape.contains_point(0.1, point.x, point.y)
}
}
pub(super) struct ButtonBuilder {
pub(super) width: Option<f32>,
pub(super) height: Option<f32>,
pub(super) position: Option<Position>,
pub(super) colors: Option<ButtonColors>,
pub(super) options: Option<DrawOptions>,
}
impl<'a> ButtonBuilder {
pub(super) fn new() -> Self {
Self {
width: None,
height: None,
position: None,
colors: None,
options: None,
}
}
pub(super) fn width(&mut self, width: f32) {
self.width = Some(width)
}
pub(super) fn height(&mut self, height: f32) {
self.height = Some(height)
}
pub(super) fn position(&mut self, x: f32, y: f32) {
self.position = Some(Position::new(x, y))
}
pub(super) fn colors(&mut self, colors: ButtonColors) {
self.colors = Some(colors)
}
pub(super) fn finish(self) -> Widget<'a> {
Widget::Button(
Button {
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"),
colors: self.colors.unwrap_or_default(),
options: self.options.unwrap_or(DrawOptions::new()),
}
)
}
}
impl Draw for Button {
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
(*area).fill_rect(
self.position.x,
self.position.y,
self.width,
self.height,
&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) 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<f32>,
pub(super) height: Option<f32>,
pub(super) position: Option<Position>,
pub(super) path: Option<&'a DrawPath>,
pub(super) colors: Option<DrawAreaColors>,
pub(super) options: Option<DrawOptions>,
}
impl<'a> DrawAreaBuilder<'a> {
pub(super) fn new() -> Self {
Self {
width: None,
height: None,
position: None,
path: None,
colors: None,
options: None,
}
}
pub(super) fn width(&mut self, width: f32) {
self.width = Some(width)
}
pub(super) fn height(&mut self, height: f32) {
self.height = Some(height)
}
pub(super) fn position(&mut self, x: f32, y: f32) {
self.position = Some(Position::new(x, y))
}
pub(super) fn colors(&mut self, colors: DrawAreaColors) {
self.colors = Some(colors)
}
pub(super) fn path(&mut self, path: &'a DrawPath) {
self.path = Some(path);
}
pub(super) fn finish(self) -> Widget<'a> {
Widget::DrawArea(
DrawArea {
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"),
path: self.path.expect("Draw must have a path"),
colors: self.colors.unwrap_or_default(),
options: self.options.unwrap_or(DrawOptions::new()),
}
)
}
}
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,
area.fill(
&self.shape,
&self.background_source(),
&self.options,
);
area.stroke(
&path,
&self.drawing_source(),
&StrokeStyle {width: 3.0, ..StrokeStyle::default()},
&self.options,
);
}
}
pub(super) struct TextAreaColors {
pub(super) background: Color,
pub(super) text: Color,
@@ -420,60 +166,9 @@ impl TextArea {
}
}
pub(super) struct TextAreaBuilder {
pub(super) width: Option<f32>,
pub(super) height: Option<f32>,
pub(super) position: Option<Position>,
pub(super) colors: Option<TextAreaColors>,
pub(super) options: Option<DrawOptions>,
}
impl<'a> TextAreaBuilder {
pub(super) fn new() -> Self {
Self {
width: None,
height: None,
position: None,
colors: None,
options: None,
}
}
pub(super) fn width(&mut self, width: f32) {
self.width = Some(width)
}
pub(super) fn height(&mut self, height: f32) {
self.height = Some(height)
}
pub(super) fn position(&mut self, x: f32, y: f32) {
self.position = Some(Position::new(x, y))
}
pub(super) fn colors(&mut self, colors: TextAreaColors) {
self.colors = Some(colors);
}
pub(super) fn finish(self) -> Widget<'a> {
Widget::TextArea(
TextArea {
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"),
colors: self.colors.unwrap_or_default(),
options: self.options.unwrap_or(DrawOptions::new()),
}
)
}
}
impl Draw for TextArea {
fn render(&self, area: &mut DrawTarget<&mut [u32]>) {
area.fill_rect(
self.position.x,
self.position.y,
@@ -482,7 +177,6 @@ impl Draw for TextArea {
&self.background_source(),
&self.options,
);
}
}