Split into more modules and add bounds checking to widgets
This commit is contained in:
158
src/main.rs
158
src/main.rs
@@ -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(())
|
||||
}
|
||||
|
||||
411
src/wayland.rs
411
src/wayland.rs
@@ -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 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 Position {
|
||||
pub(super) fn new(x: f32, y: f32) -> Self {
|
||||
Position { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
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,];
|
||||
}
|
||||
|
||||
@@ -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
187
src/wayland/builder.rs
Normal 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
132
src/wayland/draw.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,139 +29,63 @@ 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,
|
||||
self.height as f32,
|
||||
&Source::Solid(self.colors.background.into()),
|
||||
&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(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()),
|
||||
}
|
||||
)
|
||||
pub(crate) fn contains_point(&self, point: Position) -> bool {
|
||||
self.shape.contains_point(0.1, point.x, point.y)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user