244 lines
7.1 KiB
Rust
244 lines
7.1 KiB
Rust
use cosmic::{
|
|
iced::{
|
|
Alignment, Element,
|
|
core::{
|
|
Background, Border, Clipboard, Color, Event, Layout, Length, Rectangle,
|
|
Renderer as IcedRenderer, Shell, Size, layout, mouse, overlay,
|
|
renderer::{Quad, Style},
|
|
widget::{Id, Tree, Widget, tree},
|
|
},
|
|
},
|
|
widget::button::Catalog,
|
|
};
|
|
|
|
pub struct SubmenuItem<'a, Message> {
|
|
elem: cosmic::Element<'a, Message>,
|
|
idx: usize,
|
|
styling: <cosmic::Theme as Catalog>::Class,
|
|
}
|
|
|
|
impl<'a, Message> SubmenuItem<'a, Message> {
|
|
pub fn new(elem: impl Into<cosmic::Element<'a, Message>>, idx: usize) -> Self {
|
|
Self {
|
|
elem: elem.into(),
|
|
idx,
|
|
styling: Default::default(),
|
|
}
|
|
}
|
|
|
|
pub fn style(mut self, style: <cosmic::Theme as Catalog>::Class) -> Self {
|
|
self.styling = style;
|
|
self
|
|
}
|
|
}
|
|
|
|
pub trait CursorEvents {
|
|
fn cursor_entered(idx: usize, bounds: Rectangle<f32>) -> Self;
|
|
fn cursor_left(idx: usize, bounds: Rectangle<f32>) -> Self;
|
|
}
|
|
|
|
struct State {
|
|
cursor_over: bool,
|
|
}
|
|
|
|
impl<Message> Widget<Message, cosmic::Theme, cosmic::Renderer> for SubmenuItem<'_, Message>
|
|
where
|
|
Message: CursorEvents,
|
|
{
|
|
fn id(&self) -> Option<Id> {
|
|
None
|
|
}
|
|
|
|
fn size(&self) -> Size<Length> {
|
|
self.elem.as_widget().size()
|
|
}
|
|
|
|
fn layout(
|
|
&mut self,
|
|
state: &mut Tree,
|
|
renderer: &cosmic::Renderer,
|
|
limits: &layout::Limits,
|
|
) -> layout::Node {
|
|
let state = &mut state.children[0];
|
|
let node = self.elem.as_widget_mut().layout(state, renderer, limits);
|
|
layout::Node::with_children(node.size(), vec![node])
|
|
}
|
|
|
|
fn draw(
|
|
&self,
|
|
state: &Tree,
|
|
renderer: &mut cosmic::Renderer,
|
|
theme: &cosmic::Theme,
|
|
style: &Style,
|
|
layout: Layout<'_>,
|
|
cursor: mouse::Cursor,
|
|
viewport: &Rectangle,
|
|
) {
|
|
let widget_state = state.state.downcast_ref::<State>();
|
|
let mut styling = if widget_state.cursor_over {
|
|
theme.hovered(true, false, &self.styling)
|
|
} else {
|
|
theme.active(true, false, &self.styling)
|
|
};
|
|
if matches!(self.styling, cosmic::theme::Button::MenuItem) {
|
|
match theme.list_item_position {
|
|
Some((Alignment::Start, _)) => {
|
|
styling.border_radius =
|
|
styling.border_radius.bottom(theme.cosmic().radius_0()[3]);
|
|
}
|
|
Some((Alignment::End, _)) => {
|
|
styling.border_radius = styling.border_radius.top(theme.cosmic().radius_0()[0]);
|
|
}
|
|
Some((Alignment::Center, _)) => {}
|
|
None => {
|
|
styling.border_radius = theme.cosmic().radius_0().into();
|
|
}
|
|
};
|
|
}
|
|
|
|
renderer.fill_quad(
|
|
Quad {
|
|
snap: true,
|
|
bounds: layout.bounds(),
|
|
border: Border {
|
|
radius: styling.border_radius,
|
|
width: 0.,
|
|
color: styling.border_color,
|
|
},
|
|
shadow: Default::default(),
|
|
},
|
|
styling
|
|
.background
|
|
.unwrap_or(Background::Color(Color::TRANSPARENT)),
|
|
);
|
|
|
|
let state = &state.children[0];
|
|
let layout = layout.children().next().unwrap();
|
|
self.elem.as_widget().draw(
|
|
state,
|
|
renderer,
|
|
theme,
|
|
&Style {
|
|
text_color: styling.text_color.unwrap_or(style.text_color),
|
|
icon_color: styling.icon_color.unwrap_or(style.text_color),
|
|
..*style
|
|
},
|
|
layout,
|
|
cursor,
|
|
viewport,
|
|
)
|
|
}
|
|
|
|
fn tag(&self) -> tree::Tag {
|
|
tree::Tag::of::<State>()
|
|
}
|
|
|
|
fn state(&self) -> tree::State {
|
|
tree::State::new(State { cursor_over: false })
|
|
}
|
|
|
|
fn children(&self) -> Vec<Tree> {
|
|
vec![Tree::new(&self.elem)]
|
|
}
|
|
|
|
fn diff(&mut self, tree: &mut Tree) {
|
|
tree.diff_children(std::slice::from_mut(&mut self.elem))
|
|
}
|
|
|
|
fn operate(
|
|
&mut self,
|
|
state: &mut Tree,
|
|
layout: Layout<'_>,
|
|
renderer: &cosmic::Renderer,
|
|
operation: &mut dyn cosmic::widget::Operation<()>,
|
|
) {
|
|
let state = &mut state.children[0];
|
|
let layout = layout.children().next().unwrap();
|
|
self.elem
|
|
.as_widget_mut()
|
|
.operate(state, layout, renderer, operation)
|
|
}
|
|
|
|
fn update(
|
|
&mut self,
|
|
state: &mut Tree,
|
|
event: &Event,
|
|
layout: Layout<'_>,
|
|
cursor: mouse::Cursor,
|
|
renderer: &cosmic::Renderer,
|
|
clipboard: &mut dyn Clipboard,
|
|
shell: &mut Shell<'_, Message>,
|
|
viewport: &Rectangle,
|
|
) {
|
|
let mut bounds = layout.bounds();
|
|
|
|
// fix padding 1 and event... don't ask.
|
|
bounds.x -= 1.;
|
|
bounds.width += 2.;
|
|
|
|
let is_over = cursor.is_over(bounds);
|
|
let widget_state = state.state.downcast_mut::<State>();
|
|
match event {
|
|
Event::Mouse(mouse::Event::CursorEntered)
|
|
| Event::Mouse(mouse::Event::CursorMoved { .. })
|
|
if is_over && !widget_state.cursor_over =>
|
|
{
|
|
shell.publish(Message::cursor_entered(self.idx, bounds));
|
|
widget_state.cursor_over = true;
|
|
}
|
|
Event::Mouse(mouse::Event::CursorMoved { .. })
|
|
| Event::Mouse(mouse::Event::CursorLeft)
|
|
if !is_over && widget_state.cursor_over =>
|
|
{
|
|
shell.publish(Message::cursor_left(self.idx, bounds));
|
|
widget_state.cursor_over = false;
|
|
}
|
|
_ => {}
|
|
};
|
|
|
|
let state = &mut state.children[0];
|
|
let layout = layout.children().next().unwrap();
|
|
self.elem.as_widget_mut().update(
|
|
state, event, layout, cursor, renderer, clipboard, shell, viewport,
|
|
);
|
|
}
|
|
|
|
fn mouse_interaction(
|
|
&self,
|
|
state: &Tree,
|
|
layout: Layout<'_>,
|
|
cursor: mouse::Cursor,
|
|
viewport: &Rectangle,
|
|
renderer: &cosmic::Renderer,
|
|
) -> mouse::Interaction {
|
|
let state = &state.children[0];
|
|
let layout = layout.children().next().unwrap();
|
|
self.elem
|
|
.as_widget()
|
|
.mouse_interaction(state, layout, cursor, viewport, renderer)
|
|
}
|
|
|
|
fn overlay<'b>(
|
|
&'b mut self,
|
|
state: &'b mut Tree,
|
|
layout: Layout<'b>,
|
|
renderer: &cosmic::Renderer,
|
|
viewport: &Rectangle,
|
|
translation: cosmic::iced::Vector,
|
|
) -> Option<overlay::Element<'b, Message, cosmic::Theme, cosmic::Renderer>> {
|
|
let state = &mut state.children[0];
|
|
let layout = layout.children().next().unwrap();
|
|
self.elem
|
|
.as_widget_mut()
|
|
.overlay(state, layout, renderer, viewport, translation)
|
|
}
|
|
}
|
|
|
|
impl<'a, Message> From<SubmenuItem<'a, Message>> for cosmic::Element<'a, Message>
|
|
where
|
|
Message: CursorEvents + 'a,
|
|
{
|
|
fn from(val: SubmenuItem<'a, Message>) -> Self {
|
|
Element::new(val)
|
|
}
|
|
}
|