Skip to content

Commit

Permalink
status line: Custom widget for mouse input handling
Browse files Browse the repository at this point in the history
  • Loading branch information
ids1024 committed Apr 19, 2023
1 parent e22ad3f commit 5200f74
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 13 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cosmic-applet-status-line/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
delegate = "0.9"
libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["tokio", "wayland", "applet"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Expand Down
142 changes: 142 additions & 0 deletions cosmic-applet-status-line/src/bar_widget.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use cosmic::{
iced::{Length, Point, Rectangle},
iced_native::{
clipboard::Clipboard,
event::{self, Event},
layout::{Layout, Limits, Node},
mouse,
renderer::Style,
touch,
widget::{self, operation::Operation, Tree, Widget},
Shell,
},
};

use crate::protocol::ClickEvent;

const BTN_LEFT: u32 = 0x110;
const BTN_RIGHT: u32 = 0x111;
const BTN_MIDDLE: u32 = 0x112;

/// Wraps a `Row` widget, handling mouse input
pub struct BarWidget<'a, Msg> {
pub row: widget::Row<'a, Msg, cosmic::Renderer>,
pub name_instance: Vec<(Option<&'a str>, Option<&'a str>)>,
pub on_press: fn(ClickEvent) -> Msg,
}

impl<'a, Msg> Widget<Msg, cosmic::Renderer> for BarWidget<'a, Msg> {
delegate::delegate! {
to self.row {
fn children(&self) -> Vec<Tree>;
fn diff(&self, tree: &mut Tree);
fn layout(&self, renderer: &cosmic::Renderer, limits: &Limits) -> Node;
fn operate(
&self,
tree: &mut Tree,
layout: Layout<'_>,
operation: &mut dyn Operation<Msg>,
);
fn draw(
&self,
state: &Tree,
renderer: &mut cosmic::Renderer,
theme: &cosmic::Theme,
style: &Style,
layout: Layout,
cursor_position: Point,
viewport: &Rectangle,
);
}
}

fn width(&self) -> Length {
Widget::width(&self.row)
}

fn height(&self) -> Length {
Widget::height(&self.row)
}

fn on_event(
&mut self,
tree: &mut Tree,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &cosmic::Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Msg>,
) -> event::Status {
if self.update(&event, layout, cursor_position, shell) == event::Status::Captured {
return event::Status::Captured;
}
self.row.on_event(
tree,
event,
layout,
cursor_position,
renderer,
clipboard,
shell,
)
}
}

impl<'a, Msg> From<BarWidget<'a, Msg>> for cosmic::Element<'a, Msg>
where
Msg: 'a,
{
fn from(widget: BarWidget<'a, Msg>) -> cosmic::Element<'a, Msg> {
cosmic::Element::new(widget)
}
}

impl<'a, Msg> BarWidget<'a, Msg> {
fn update(
&mut self,
event: &Event,
layout: Layout<'_>,
cursor_position: Point,
shell: &mut Shell<'_, Msg>,
) -> event::Status {
let (button, event_code) = match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => (1, BTN_LEFT),
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle)) => (2, BTN_MIDDLE),
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) => (3, BTN_RIGHT),
Event::Touch(touch::Event::FingerPressed { .. }) => (1, BTN_LEFT),
_ => {
return event::Status::Ignored;
}
};

let Some((n, bounds)) = layout.children().map(|x| x.bounds()).enumerate().find(|(_, bounds)| bounds.contains(cursor_position)) else {
return event::Status::Ignored;
};

let (name, instance) = self.name_instance.get(n).cloned().unwrap_or((None, None));

// TODO coordinate space? int conversion?
let x = cursor_position.x as u32;
let y = cursor_position.y as u32;
let relative_x = (cursor_position.x - bounds.x) as u32;
let relative_y = (cursor_position.y - bounds.y) as u32;
let width = bounds.width as u32;
let height = bounds.height as u32;

shell.publish((self.on_press)(ClickEvent {
name: name.map(str::to_owned),
instance: instance.map(str::to_owned),
x,
y,
button,
event: event_code,
relative_x,
relative_y,
width,
height,
}));

event::Status::Captured
}
}
27 changes: 26 additions & 1 deletion cosmic-applet-status-line/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ use cosmic::{
iced_style::application,
};

mod bar_widget;
use bar_widget::BarWidget;
mod protocol;

#[derive(Debug)]
enum Msg {
Protocol(protocol::StatusLine),
ClickEvent(protocol::ClickEvent),
CloseRequest,
}

Expand Down Expand Up @@ -56,13 +59,35 @@ impl iced::Application for App {
println!("{:?}", status_line);
self.status_line = status_line;
}
Msg::ClickEvent(click_event) => {
println!("{:?}", click_event);
if self.status_line.click_events {
// TODO: pass click event to backend
}
}
Msg::CloseRequest => {}
}
iced::Command::none()
}

fn view(&self, _id: window::Id) -> cosmic::Element<Msg> {
iced::widget::row(self.status_line.blocks.iter().map(block_view).collect()).into()
let (block_views, name_instance): (Vec<_>, Vec<_>) = self
.status_line
.blocks
.iter()
.map(|block| {
(
block_view(block),
(block.name.as_deref(), block.instance.as_deref()),
)
})
.unzip();
BarWidget {
row: iced::widget::row(block_views),
name_instance,
on_press: Msg::ClickEvent,
}
.into()
}
}

Expand Down
2 changes: 1 addition & 1 deletion cosmic-applet-status-line/src/protocol/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use std::{
use tokio::sync::mpsc;

mod serialization;
pub use serialization::Block;
use serialization::Header;
pub use serialization::{Block, ClickEvent};

#[derive(Debug, Default)]
pub struct StatusLine {
Expand Down
22 changes: 11 additions & 11 deletions cosmic-applet-status-line/src/protocol/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,15 @@ pub struct Block {
}

#[derive(Clone, Debug, serde::Serialize)]
struct ClickEvent {
name: Option<String>,
instance: Option<String>,
x: u32,
y: u32,
button: u32,
event: u32,
relative_x: u32,
relative_y: u32,
width: u32,
height: u32,
pub struct ClickEvent {
pub name: Option<String>,
pub instance: Option<String>,
pub x: u32,
pub y: u32,
pub button: u32,
pub event: u32,
pub relative_x: u32,
pub relative_y: u32,
pub width: u32,
pub height: u32,
}

0 comments on commit 5200f74

Please sign in to comment.