Compare commits

..

4 commits

Author SHA1 Message Date
TheOddGarlic 4a4b94ba2a click events! soon there will be buttons! 2022-05-22 19:23:58 +03:00
TheOddGarlic dbf64e8b7f trait IntoPadded -> trait Padded
I have no idea what was going through my mind while naming this.
2022-05-22 13:53:16 +03:00
TheOddGarlic 098a0b6df8 clear debug prints 2022-05-22 12:54:33 +03:00
TheOddGarlic 0649c53695 new window resize event 2022-05-22 12:52:26 +03:00
16 changed files with 339 additions and 61 deletions

View file

@ -22,7 +22,6 @@ pub struct Renderer {
impl Renderer { impl Renderer {
pub fn new(handle: XlibHandle) -> Self { pub fn new(handle: XlibHandle) -> Self {
println!("{:#?}", handle);
let (width, height) = unsafe { let (width, height) = unsafe {
let mut root = mem::zeroed(); let mut root = mem::zeroed();
let mut x = mem::zeroed(); let mut x = mem::zeroed();
@ -165,27 +164,10 @@ impl Renderer {
} }
pub fn size(&self) -> (u32, u32) { pub fn size(&self) -> (u32, u32) {
unsafe { unsafe {(
let mut root = mem::zeroed(); cairo_xlib_surface_get_width(self.surface).try_into().unwrap(),
let mut x = mem::zeroed(); cairo_xlib_surface_get_height(self.surface).try_into().unwrap(),
let mut y = mem::zeroed(); )}
let mut width = mem::zeroed();
let mut height = mem::zeroed();
let mut border_width = mem::zeroed();
let mut depth = mem::zeroed();
assert_ne!(XGetGeometry(
self.handle.display as _,
self.handle.window.try_into().unwrap(),
&mut root,
&mut x,
&mut y,
&mut width,
&mut height,
&mut border_width,
&mut depth,
), 0);
(width, height)
}
} }
fn text_extents(&self, text: &str) -> TextExtents { fn text_extents(&self, text: &str) -> TextExtents {

View file

@ -62,7 +62,6 @@ impl Renderer {
} }
pub fn draw_text<S: AsRef<str>>(&self, text: S) { pub fn draw_text<S: AsRef<str>>(&self, text: S) {
eprintln!("drawing text at ({}, {})", self.x, self.y);
self.renderer.draw_text( self.renderer.draw_text(
text.as_ref(), text.as_ref(),
Rect::new( Rect::new(

View file

@ -39,7 +39,7 @@ impl Renderer {
.unwrap() .unwrap()
}; };
Self { let mut renderer = Self {
handle, handle,
factory, factory,
dw_factory, dw_factory,
@ -48,12 +48,12 @@ impl Renderer {
stroke_style, stroke_style,
system_fonts, system_fonts,
text_format, text_format,
} };
renderer.setup_target();
renderer
} }
pub fn begin_draw(&mut self) { pub fn begin_draw(&mut self) {
self.setup_target();
unsafe { self.target.as_ref().unwrap().BeginDraw() } unsafe { self.target.as_ref().unwrap().BeginDraw() }
} }

View file

@ -9,7 +9,7 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use winit::event::{Event, WindowEvent as WinitWindowEvent}; use winit::event::{Event, WindowEvent as WinitWindowEvent, ElementState};
use winit::event_loop::EventLoop; use winit::event_loop::EventLoop;
use winit::window::WindowId; use winit::window::WindowId;
use crate::context::Context; use crate::context::Context;
@ -104,17 +104,42 @@ impl Application {
self.windows.remove(&window_id); self.windows.remove(&window_id);
if self.windows.is_empty() { if self.windows.is_empty() {
emit_app_event!(self, ApplicationEvent::AllWindowsClosed); emit_app_event!(self, ApplicationEvent::AllWindowsDestroyed);
} }
} }
Event::WindowEvent { Event::WindowEvent {
window_id, window_id,
event: WinitWindowEvent::Resized(size) event: WinitWindowEvent::Resized(size)
} => self.windows.get_mut(&window_id).unwrap().resized(size), } => self.windows.get_mut(&window_id).unwrap().resized(size),
Event::WindowEvent {
window_id,
event: WinitWindowEvent::CursorMoved {
position,
..
},
} => self.windows.get_mut(&window_id).unwrap().cursor_moved(position),
Event::WindowEvent {
window_id,
event: WinitWindowEvent::MouseInput {
state,
button,
..
},
} => match state {
ElementState::Pressed => self.windows
.get_mut(&window_id).unwrap().mouse_pressed(button),
ElementState::Released => self.windows
.get_mut(&window_id).unwrap().mouse_released(button),
}
Event::MainEventsCleared => { Event::MainEventsCleared => {
// determine if state changed and request redraw if needed // determine if state changed and request redraw if needed
// rinse and repeat for every window // rinse and repeat for every window
} }
Event::RedrawRequested(window_id) => Event::RedrawRequested(window_id) =>
self.windows.get_mut(&window_id).unwrap().render(), self.windows.get_mut(&window_id).unwrap().render(),

View file

@ -9,5 +9,5 @@
#[non_exhaustive] #[non_exhaustive]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Event { pub enum Event {
AllWindowsClosed, AllWindowsDestroyed,
} }

View file

@ -10,4 +10,5 @@
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Event { pub enum Event {
Closed, Closed,
Resized,
} }

View file

@ -19,6 +19,10 @@ impl Position {
Position { x, y, width, height } Position { x, y, width, height }
} }
pub fn contains(&self, x: u32, y: u32) -> bool {
x >= self.x && y >= self.y && x < self.x + self.width && y < self.y + self.height
}
pub fn x(&self) -> u32 { pub fn x(&self) -> u32 {
self.x self.x
} }

View file

@ -24,7 +24,7 @@ pub mod prelude {
pub use crate::event::window::Event as WindowEvent; pub use crate::event::window::Event as WindowEvent;
pub use crate::platform::Platform; pub use crate::platform::Platform;
pub use crate::widget::Column; pub use crate::widget::Column;
pub use crate::widget::IntoPadded; pub use crate::widget::Padded;
pub use crate::widget::Label; pub use crate::widget::Label;
pub use crate::widget::Row; pub use crate::widget::Row;
pub use crate::widget::Padding; pub use crate::widget::Padding;

View file

@ -20,10 +20,10 @@ fn launch() -> _ {
.add(Column::new() .add(Column::new()
.add(Label::new("World!")) .add(Label::new("World!"))
.add(Label::new("AbleTK!"))) .add(Label::new("AbleTK!")))
.add(Label::new("this is a label! jjjjyyy") .add(Label::new("this is a label!")
.bg_color(rgb!(0xFF0000FF))) .bg_color(rgb!(0xFF0000FF)))
.padding_left(10)) .padding_left(10))
.on_event(WindowEvent::Closed, |_, window| { .on_event(WindowEvent::Resized, |_, window| {
window.set_title("CLOSING!") println!("window resized: {:?}", window.size())
})) }))
} }

View file

@ -25,7 +25,7 @@ pub struct QuitPlugin;
impl Plugin for QuitPlugin { impl Plugin for QuitPlugin {
fn apply(&self, app: Application) -> Application { fn apply(&self, app: Application) -> Application {
app.on_event(Event::AllWindowsClosed, |ctx| { app.on_event(Event::AllWindowsDestroyed, |ctx| {
if Platform::target() != Platform::MacOS { if Platform::target() != Platform::MacOS {
ctx.quit() ctx.quit()
} }

View file

@ -6,8 +6,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
use crate::{layout::position::Position, widget::Widget}; use crate::{layout::position::Position, widget::Widget, window::Window};
use abletk_common::Renderer; use abletk_common::Renderer;
use winit::event::MouseButton;
pub struct Column { pub struct Column {
widgets: Vec<Box<dyn Widget>>, widgets: Vec<Box<dyn Widget>>,
@ -43,6 +44,78 @@ impl Widget for Column {
Position::new(0, 0, width, height) Position::new(0, 0, width, height)
} }
fn mouse_pressed(&mut self,
renderer: &mut Renderer,
window: &mut Window,
button: MouseButton,
x: u32,
y: u32,
) {
let mut pos = Position::new(0, 0, 0, 0);
for widget in &mut self.widgets {
let widget_pos = widget.position(renderer);
pos = Position::new(
widget_pos.x(),
pos.y() + widget_pos.y(),
widget_pos.width(),
widget_pos.height(),
);
if pos.contains(x, y) {
widget.mouse_pressed(
renderer,
window,
button,
x - pos.x(),
y - pos.y(),
);
}
pos = Position::new(
pos.x(),
pos.y() + widget_pos.height(),
pos.width(),
pos.height(),
)
}
}
fn mouse_released(&mut self,
renderer: &mut Renderer,
window: &mut Window,
button: MouseButton,
x: u32,
y: u32,
) {
let mut pos = Position::new(0, 0, 0, 0);
for widget in &mut self.widgets {
let widget_pos = widget.position(renderer);
pos = Position::new(
widget_pos.x(),
pos.y() + widget_pos.y(),
widget_pos.width(),
widget_pos.height(),
);
if pos.contains(x, y) {
widget.mouse_released(
renderer,
window,
button,
x - pos.x(),
y - pos.y(),
);
}
pos = Position::new(
pos.x(),
pos.y() + widget_pos.height(),
pos.width(),
pos.height(),
)
}
}
} }
impl Column { impl Column {

View file

@ -7,7 +7,8 @@
*/ */
use abletk_common::{Renderer, brush::Brush, color::Color, rgb}; use abletk_common::{Renderer, brush::Brush, color::Color, rgb};
use crate::{widget::Widget, layout::position::Position}; use winit::event::MouseButton;
use crate::{widget::Widget, layout::position::Position, window::Window};
pub struct Label { pub struct Label {
text: String, text: String,
@ -28,6 +29,26 @@ impl Widget for Label {
let (width, height) = renderer.get_text_size(&self.text); let (width, height) = renderer.get_text_size(&self.text);
Position::new(0, 0, width, height) Position::new(0, 0, width, height)
} }
fn mouse_pressed(&mut self,
renderer: &mut Renderer,
window: &mut Window,
button: MouseButton,
x: u32,
y: u32,
) {
eprintln!("Label::mouse_pressed {} {x}, {y}", self.text);
}
fn mouse_released(&mut self,
renderer: &mut Renderer,
window: &mut Window,
button: MouseButton,
x: u32,
y: u32,
) {
eprintln!("Label::mouse_released {} {x}, {y}", self.text);
}
} }
impl Label { impl Label {

View file

@ -16,11 +16,29 @@ pub use column::*;
pub use label::*; pub use label::*;
pub use padding::*; pub use padding::*;
pub use row::*; pub use row::*;
use winit::event::MouseButton;
use crate::layout::position::Position; use crate::{layout::position::Position, window::Window};
pub trait Widget { pub trait Widget {
fn draw(&self, renderer: &mut Renderer); fn draw(&self, renderer: &mut Renderer);
// fixme: don't pass renderer // fixme: don't pass renderer
fn position(&self, renderer: &mut Renderer) -> Position; fn position(&self, renderer: &mut Renderer) -> Position;
fn mouse_pressed(&mut self,
renderer: &mut Renderer,
window: &mut Window,
button: MouseButton,
x: u32,
y: u32,
);
fn mouse_released(&mut self,
renderer: &mut Renderer,
window: &mut Window,
button: MouseButton,
x: u32,
y: u32,
);
} }

View file

@ -6,8 +6,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
use crate::{layout::position::Position, widget::Widget}; use crate::{layout::position::Position, widget::Widget, window::Window};
use abletk_common::Renderer; use abletk_common::Renderer;
use winit::event::MouseButton;
pub struct Padding<W: Widget> { pub struct Padding<W: Widget> {
widget: W, widget: W,
@ -32,6 +33,32 @@ impl<W: Widget> Widget for Padding<W> {
pos.height() + self.bottom, pos.height() + self.bottom,
) )
} }
fn mouse_pressed(&mut self,
renderer: &mut Renderer,
window: &mut Window,
button: MouseButton,
x: u32,
y: u32,
) {
let pos = self.position(renderer);
if pos.contains(x, y) {
self.widget.mouse_pressed(renderer, window, button, x - pos.x(), y - pos.y());
}
}
fn mouse_released(&mut self,
renderer: &mut Renderer,
window: &mut Window,
button: MouseButton,
x: u32,
y: u32,
) {
let pos = self.position(renderer);
if pos.contains(x, y) {
self.widget.mouse_released(renderer, window, button, x - pos.x(), y - pos.y());
}
}
} }
impl<W: Widget> Padding<W> { impl<W: Widget> Padding<W> {
@ -52,7 +79,7 @@ impl<W: Widget> Padding<W> {
} }
} }
pub trait IntoPadded<W: Widget> { pub trait Padded<W: Widget> {
fn padding(self, left: u32, right: u32, top: u32, bottom: u32) -> Padding<W>; fn padding(self, left: u32, right: u32, top: u32, bottom: u32) -> Padding<W>;
fn padding_x(self, left: u32, right: u32) -> Padding<W>; fn padding_x(self, left: u32, right: u32) -> Padding<W>;
fn padding_y(self, top: u32, bottom: u32) -> Padding<W>; fn padding_y(self, top: u32, bottom: u32) -> Padding<W>;
@ -63,7 +90,7 @@ pub trait IntoPadded<W: Widget> {
fn padding_all(self, padding: u32) -> Padding<W>; fn padding_all(self, padding: u32) -> Padding<W>;
} }
impl<W: Widget> IntoPadded<W> for W { impl<W: Widget> Padded<W> for W {
fn padding(self, left: u32, right: u32, top: u32, bottom: u32) -> Padding<W> { fn padding(self, left: u32, right: u32, top: u32, bottom: u32) -> Padding<W> {
Padding::new(left, right, top, bottom, self) Padding::new(left, right, top, bottom, self)
} }

View file

@ -6,8 +6,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
use crate::{layout::position::Position, widget::Widget}; use crate::{layout::position::Position, widget::Widget, window::Window};
use abletk_common::Renderer; use abletk_common::Renderer;
use winit::event::MouseButton;
pub struct Row { pub struct Row {
widgets: Vec<Box<dyn Widget>>, widgets: Vec<Box<dyn Widget>>,
@ -43,6 +44,78 @@ impl Widget for Row {
Position::new(0, 0, width, height) Position::new(0, 0, width, height)
} }
fn mouse_pressed(&mut self,
renderer: &mut Renderer,
window: &mut Window,
button: MouseButton,
x: u32,
y: u32,
) {
let mut pos = Position::new(0, 0, 0, 0);
for widget in &mut self.widgets {
let widget_pos = widget.position(renderer);
pos = Position::new(
widget_pos.x() + pos.x(),
widget_pos.y(),
widget_pos.width(),
widget_pos.height(),
);
if pos.contains(x, y) {
widget.mouse_pressed(
renderer,
window,
button,
x - pos.x(),
y - pos.y(),
);
}
pos = Position::new(
pos.x() + widget_pos.width(),
pos.y(),
pos.width(),
pos.height(),
);
}
}
fn mouse_released(&mut self,
renderer: &mut Renderer,
window: &mut Window,
button: MouseButton,
x: u32,
y: u32,
) {
let mut pos = Position::new(0, 0, 0, 0);
for widget in &mut self.widgets {
let widget_pos = widget.position(renderer);
pos = Position::new(
widget_pos.x() + pos.x(),
widget_pos.y(),
widget_pos.width(),
widget_pos.height(),
);
if pos.contains(x, y) {
widget.mouse_released(
renderer,
window,
button,
x - pos.x(),
y - pos.y(),
);
}
pos = Position::new(
pos.x() + widget_pos.width(),
pos.y(),
pos.width(),
pos.height(),
);
}
}
} }
impl Row { impl Row {

View file

@ -12,7 +12,7 @@ use std::rc::Rc;
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
use winit::{ use winit::{
event_loop::EventLoopWindowTarget, event_loop::EventLoopWindowTarget,
window::{Window as WinitWindow, WindowBuilder as WinitWindowBuilder}, window::{Window as WinitWindow, WindowBuilder as WinitWindowBuilder}, dpi::PhysicalPosition, event::MouseButton,
}; };
use winit::dpi::PhysicalSize; use winit::dpi::PhysicalSize;
use winit::error::OsError; use winit::error::OsError;
@ -26,10 +26,11 @@ use crate::widget::Widget;
pub struct Window { pub struct Window {
window: WinitWindow, window: WinitWindow,
events: HashMap<Event, Vec<fn(&mut Context, &mut Window)>>, events: HashMap<Event, Vec<fn(&mut Context, &mut Window)>>,
root: Box<dyn Widget>, root: Option<Box<dyn Widget>>,
ctx: Rc<RefCell<Context>>, ctx: Rc<RefCell<Context>>,
renderer: Renderer, renderer: Option<Renderer>,
background: Color, background: Color,
cursor_pos: (u32, u32),
} }
impl Window { impl Window {
@ -58,12 +59,13 @@ impl Window {
.build(event_loop)?; .build(event_loop)?;
Ok(Self { Ok(Self {
renderer: Renderer::new(window.raw_window_handle()), renderer: Some(Renderer::new(window.raw_window_handle())),
window, window,
background, background,
events, events,
root, root: Some(root),
ctx, ctx,
cursor_pos: (0, 0),
}) })
} }
@ -72,19 +74,14 @@ impl Window {
} }
pub(crate) fn render(&mut self) { pub(crate) fn render(&mut self) {
let position = self.root.position(&mut self.renderer); let root = self.root.as_ref().unwrap();
eprintln!("Rendering window with id {:?}", self.id()); let renderer = self.renderer.as_mut().unwrap();
eprintln!("Root widget position: {position:?}"); let position = root.position(renderer);
renderer.begin_draw();
self.renderer.begin_draw(); renderer.clear(self.background);
self.renderer.clear(self.background); renderer.position_at(position.x(), position.y());
self.renderer.position_at(position.x(), position.y()); root.draw(renderer);
self.root.draw(&mut self.renderer); renderer.end_draw()
self.renderer.end_draw()
}
pub fn set_always_on_top(&self, value: bool) {
self.window.set_always_on_top(value)
} }
pub fn focus(&self) { pub fn focus(&self) {
@ -95,10 +92,19 @@ impl Window {
self.window.request_redraw() self.window.request_redraw()
} }
pub fn set_always_on_top(&self, value: bool) {
self.window.set_always_on_top(value)
}
pub fn set_title<S: AsRef<str> + Into<String>>(&mut self, title: S) { pub fn set_title<S: AsRef<str> + Into<String>>(&mut self, title: S) {
self.window.set_title(title.as_ref()) self.window.set_title(title.as_ref())
} }
pub fn size(&self) -> (u32, u32) {
let size = self.window.inner_size();
(size.width, size.height)
}
pub(crate) fn emit_event(&mut self, event: Event) { pub(crate) fn emit_event(&mut self, event: Event) {
if let Some(handlers) = self.events.get(&event) { if let Some(handlers) = self.events.get(&event) {
let ctx = self.ctx.clone(); let ctx = self.ctx.clone();
@ -107,12 +113,61 @@ impl Window {
} }
} }
pub(crate) fn cursor_moved(&mut self, pos: PhysicalPosition<f64>) {
self.cursor_pos = (pos.x as u32, pos.y as u32)
}
pub(crate) fn id(&self) -> WindowId { pub(crate) fn id(&self) -> WindowId {
self.window.id() self.window.id()
} }
pub(crate) fn mouse_pressed(&mut self, button: MouseButton) {
if self.root
.as_ref()
.unwrap()
.position(self.renderer.as_mut().unwrap())
.contains(self.cursor_pos.0, self.cursor_pos.1)
{
let mut root = self.root.take().unwrap();
let mut renderer = self.renderer.take().unwrap();
root.mouse_pressed(
&mut renderer,
self,
button,
self.cursor_pos.0,
self.cursor_pos.1,
);
self.root = Some(root);
self.renderer = Some(renderer);
}
}
pub(crate) fn mouse_released(&mut self, button: MouseButton) {
if self.root
.as_ref()
.unwrap()
.position(self.renderer.as_mut().unwrap())
.contains(self.cursor_pos.0, self.cursor_pos.1)
{
let mut root = self.root.take().unwrap();
let mut renderer = self.renderer.take().unwrap();
root.mouse_released(
&mut renderer,
self,
button,
self.cursor_pos.0,
self.cursor_pos.1,
);
self.root = Some(root);
self.renderer = Some(renderer);
}
}
pub(crate) fn resized(&mut self, size: PhysicalSize<u32>) { pub(crate) fn resized(&mut self, size: PhysicalSize<u32>) {
self.renderer.resized(size.width, size.height) self.renderer.as_mut().unwrap().resized(size.width, size.height);
self.emit_event(Event::Resized)
} }
} }