Merge pull request #603 from Koranir/highlight-on-mouse-over
feat: Highlight items on mouse over
This commit is contained in:
commit
e85cfdbfe2
3 changed files with 129 additions and 10 deletions
|
|
@ -133,6 +133,7 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
||||||
pos_opt: Cell::new(None),
|
pos_opt: Cell::new(None),
|
||||||
rect_opt: Cell::new(None),
|
rect_opt: Cell::new(None),
|
||||||
selected: false,
|
selected: false,
|
||||||
|
highlighted: false,
|
||||||
overlaps_drag_rect: false,
|
overlaps_drag_rect: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ pub struct MouseArea<'a, Message> {
|
||||||
on_forward_press: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
|
on_forward_press: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
|
||||||
on_forward_release: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
|
on_forward_release: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
|
||||||
on_scroll: Option<Box<dyn Fn(mouse::ScrollDelta, Modifiers) -> Option<Message> + 'a>>,
|
on_scroll: Option<Box<dyn Fn(mouse::ScrollDelta, Modifiers) -> Option<Message> + 'a>>,
|
||||||
|
on_enter: Option<Box<dyn Fn() -> Message + 'a>>,
|
||||||
|
on_exit: Option<Box<dyn Fn() -> Message + 'a>>,
|
||||||
show_drag_rect: bool,
|
show_drag_rect: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,6 +171,20 @@ impl<'a, Message> MouseArea<'a, Message> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The message to emit when a mouse enters the area.
|
||||||
|
#[must_use]
|
||||||
|
pub fn on_enter(mut self, message: impl Fn() -> Message + 'a) -> Self {
|
||||||
|
self.on_enter = Some(Box::new(message));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The message to emit when a mouse exits the area.
|
||||||
|
#[must_use]
|
||||||
|
pub fn on_exit(mut self, message: impl Fn() -> Message + 'a) -> Self {
|
||||||
|
self.on_exit = Some(Box::new(message));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn show_drag_rect(mut self, show_drag_rect: bool) -> Self {
|
pub fn show_drag_rect(mut self, show_drag_rect: bool) -> Self {
|
||||||
self.show_drag_rect = show_drag_rect;
|
self.show_drag_rect = show_drag_rect;
|
||||||
|
|
@ -186,7 +202,7 @@ impl<'a, Message> MouseArea<'a, Message> {
|
||||||
/// Local state of the [`MouseArea`].
|
/// Local state of the [`MouseArea`].
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct State {
|
struct State {
|
||||||
// TODO: Support on_mouse_enter and on_mouse_exit
|
last_position: Option<Point>,
|
||||||
drag_initiated: Option<Point>,
|
drag_initiated: Option<Point>,
|
||||||
modifiers: Modifiers,
|
modifiers: Modifiers,
|
||||||
prev_click: Option<(mouse::Click, Instant)>,
|
prev_click: Option<(mouse::Click, Instant)>,
|
||||||
|
|
@ -260,6 +276,8 @@ impl<'a, Message> MouseArea<'a, Message> {
|
||||||
on_back_release: None,
|
on_back_release: None,
|
||||||
on_forward_press: None,
|
on_forward_press: None,
|
||||||
on_forward_release: None,
|
on_forward_release: None,
|
||||||
|
on_enter: None,
|
||||||
|
on_exit: None,
|
||||||
on_scroll: None,
|
on_scroll: None,
|
||||||
show_drag_rect: false,
|
show_drag_rect: false,
|
||||||
}
|
}
|
||||||
|
|
@ -474,6 +492,24 @@ fn update<Message: Clone>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Event::Mouse(mouse::Event::CursorMoved { .. }) = event {
|
||||||
|
let position_in = cursor.position_in(layout_bounds);
|
||||||
|
match (position_in, state.last_position) {
|
||||||
|
(None, Some(_)) => {
|
||||||
|
if let Some(message) = widget.on_exit.as_ref() {
|
||||||
|
shell.publish(message())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(new), None) => {
|
||||||
|
if let Some(message) = widget.on_enter.as_ref() {
|
||||||
|
shell.publish(message())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
state.last_position = position_in;
|
||||||
|
}
|
||||||
|
|
||||||
if state.drag_initiated.is_none() && !cursor.is_over(layout_bounds) {
|
if state.drag_initiated.is_none() && !cursor.is_over(layout_bounds) {
|
||||||
return event::Status::Ignored;
|
return event::Status::Ignored;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
100
src/tab.rs
100
src/tab.rs
|
|
@ -118,6 +118,7 @@ static SPECIAL_DIRS: Lazy<HashMap<PathBuf, &'static str>> = Lazy::new(|| {
|
||||||
fn button_appearance(
|
fn button_appearance(
|
||||||
theme: &theme::Theme,
|
theme: &theme::Theme,
|
||||||
selected: bool,
|
selected: bool,
|
||||||
|
highlighted: bool,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
accent: bool,
|
accent: bool,
|
||||||
condensed_radius: bool,
|
condensed_radius: bool,
|
||||||
|
|
@ -133,6 +134,14 @@ fn button_appearance(
|
||||||
} else {
|
} else {
|
||||||
appearance.background = Some(Color::from(cosmic.bg_component_color()).into());
|
appearance.background = Some(Color::from(cosmic.bg_component_color()).into());
|
||||||
}
|
}
|
||||||
|
} else if highlighted {
|
||||||
|
if accent {
|
||||||
|
appearance.background = Some(Color::from(cosmic.bg_component_color()).into());
|
||||||
|
appearance.icon_color = Some(Color::from(cosmic.on_bg_component_color()));
|
||||||
|
appearance.text_color = Some(Color::from(cosmic.on_bg_component_color()));
|
||||||
|
} else {
|
||||||
|
appearance.background = Some(Color::from(cosmic.bg_component_color()).into());
|
||||||
|
}
|
||||||
} else if desktop {
|
} else if desktop {
|
||||||
appearance.background = Some(Color::from(cosmic.bg_color()).into());
|
appearance.background = Some(Color::from(cosmic.bg_color()).into());
|
||||||
appearance.icon_color = Some(Color::from(cosmic.on_bg_color()));
|
appearance.icon_color = Some(Color::from(cosmic.on_bg_color()));
|
||||||
|
|
@ -154,6 +163,7 @@ fn button_appearance(
|
||||||
|
|
||||||
fn button_style(
|
fn button_style(
|
||||||
selected: bool,
|
selected: bool,
|
||||||
|
highlighted: bool,
|
||||||
accent: bool,
|
accent: bool,
|
||||||
condensed_radius: bool,
|
condensed_radius: bool,
|
||||||
desktop: bool,
|
desktop: bool,
|
||||||
|
|
@ -161,16 +171,48 @@ fn button_style(
|
||||||
//TODO: move to libcosmic?
|
//TODO: move to libcosmic?
|
||||||
theme::Button::Custom {
|
theme::Button::Custom {
|
||||||
active: Box::new(move |focused, theme| {
|
active: Box::new(move |focused, theme| {
|
||||||
button_appearance(theme, selected, focused, accent, condensed_radius, desktop)
|
button_appearance(
|
||||||
|
theme,
|
||||||
|
selected,
|
||||||
|
highlighted,
|
||||||
|
focused,
|
||||||
|
accent,
|
||||||
|
condensed_radius,
|
||||||
|
desktop,
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
disabled: Box::new(move |theme| {
|
disabled: Box::new(move |theme| {
|
||||||
button_appearance(theme, selected, false, accent, condensed_radius, desktop)
|
button_appearance(
|
||||||
|
theme,
|
||||||
|
selected,
|
||||||
|
highlighted,
|
||||||
|
false,
|
||||||
|
accent,
|
||||||
|
condensed_radius,
|
||||||
|
desktop,
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
hovered: Box::new(move |focused, theme| {
|
hovered: Box::new(move |focused, theme| {
|
||||||
button_appearance(theme, selected, focused, accent, condensed_radius, desktop)
|
button_appearance(
|
||||||
|
theme,
|
||||||
|
selected,
|
||||||
|
highlighted,
|
||||||
|
focused,
|
||||||
|
accent,
|
||||||
|
condensed_radius,
|
||||||
|
desktop,
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
pressed: Box::new(move |focused, theme| {
|
pressed: Box::new(move |focused, theme| {
|
||||||
button_appearance(theme, selected, focused, accent, condensed_radius, desktop)
|
button_appearance(
|
||||||
|
theme,
|
||||||
|
selected,
|
||||||
|
highlighted,
|
||||||
|
focused,
|
||||||
|
accent,
|
||||||
|
condensed_radius,
|
||||||
|
desktop,
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -444,6 +486,7 @@ pub fn item_from_entry(
|
||||||
pos_opt: Cell::new(None),
|
pos_opt: Cell::new(None),
|
||||||
rect_opt: Cell::new(None),
|
rect_opt: Cell::new(None),
|
||||||
selected: false,
|
selected: false,
|
||||||
|
highlighted: false,
|
||||||
overlaps_drag_rect: false,
|
overlaps_drag_rect: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -672,6 +715,7 @@ pub fn scan_trash(sizes: IconSizes) -> Vec<Item> {
|
||||||
pos_opt: Cell::new(None),
|
pos_opt: Cell::new(None),
|
||||||
rect_opt: Cell::new(None),
|
rect_opt: Cell::new(None),
|
||||||
selected: false,
|
selected: false,
|
||||||
|
highlighted: false,
|
||||||
overlaps_drag_rect: false,
|
overlaps_drag_rect: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -854,6 +898,7 @@ pub fn scan_desktop(
|
||||||
pos_opt: Cell::new(None),
|
pos_opt: Cell::new(None),
|
||||||
rect_opt: Cell::new(None),
|
rect_opt: Cell::new(None),
|
||||||
selected: false,
|
selected: false,
|
||||||
|
highlighted: false,
|
||||||
overlaps_drag_rect: false,
|
overlaps_drag_rect: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -1022,6 +1067,8 @@ pub enum Message {
|
||||||
WindowToggleMaximize,
|
WindowToggleMaximize,
|
||||||
ZoomIn,
|
ZoomIn,
|
||||||
ZoomOut,
|
ZoomOut,
|
||||||
|
HighlightDeactivate(usize),
|
||||||
|
HighlightActivate(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
|
@ -1247,6 +1294,7 @@ pub struct Item {
|
||||||
pub pos_opt: Cell<Option<(usize, usize)>>,
|
pub pos_opt: Cell<Option<(usize, usize)>>,
|
||||||
pub rect_opt: Cell<Option<Rectangle>>,
|
pub rect_opt: Cell<Option<Rectangle>>,
|
||||||
pub selected: bool,
|
pub selected: bool,
|
||||||
|
pub highlighted: bool,
|
||||||
pub overlaps_drag_rect: bool,
|
pub overlaps_drag_rect: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2592,6 +2640,16 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Message::HighlightDeactivate(i) => {
|
||||||
|
if let Some(item) = self.items_opt.as_mut().and_then(|f| f.get_mut(i)) {
|
||||||
|
item.highlighted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::HighlightActivate(i) => {
|
||||||
|
if let Some(item) = self.items_opt.as_mut().and_then(|f| f.get_mut(i)) {
|
||||||
|
item.highlighted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Message::Scroll(viewport) => {
|
Message::Scroll(viewport) => {
|
||||||
self.scroll_opt = Some(viewport.absolute_offset());
|
self.scroll_opt = Some(viewport.absolute_offset());
|
||||||
|
|
@ -3554,7 +3612,13 @@ impl Tab {
|
||||||
.size(icon_sizes.grid()),
|
.size(icon_sizes.grid()),
|
||||||
)
|
)
|
||||||
.padding(space_xxxs)
|
.padding(space_xxxs)
|
||||||
.class(button_style(item.selected, false, false, false))
|
.class(button_style(
|
||||||
|
item.selected,
|
||||||
|
item.highlighted,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
))
|
||||||
.into(),
|
.into(),
|
||||||
widget::tooltip(
|
widget::tooltip(
|
||||||
widget::button::custom(widget::text::body(&item.display_name))
|
widget::button::custom(widget::text::body(&item.display_name))
|
||||||
|
|
@ -3562,6 +3626,7 @@ impl Tab {
|
||||||
.padding([0, space_xxxs])
|
.padding([0, space_xxxs])
|
||||||
.class(button_style(
|
.class(button_style(
|
||||||
item.selected,
|
item.selected,
|
||||||
|
item.highlighted,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
matches!(self.mode, Mode::Desktop),
|
matches!(self.mode, Mode::Desktop),
|
||||||
|
|
@ -3606,7 +3671,9 @@ impl Tab {
|
||||||
.on_press(move |_| Message::Click(Some(i)))
|
.on_press(move |_| Message::Click(Some(i)))
|
||||||
.on_double_click(move |_| Message::DoubleClick(Some(i)))
|
.on_double_click(move |_| Message::DoubleClick(Some(i)))
|
||||||
.on_release(move |_| Message::ClickRelease(Some(i)))
|
.on_release(move |_| Message::ClickRelease(Some(i)))
|
||||||
.on_middle_press(move |_| Message::MiddleClick(i));
|
.on_middle_press(move |_| Message::MiddleClick(i))
|
||||||
|
.on_enter(move || Message::HighlightActivate(i))
|
||||||
|
.on_exit(move || Message::HighlightDeactivate(i));
|
||||||
|
|
||||||
//TODO: error if the row or col is already set?
|
//TODO: error if the row or col is already set?
|
||||||
while grid_elements.len() <= row {
|
while grid_elements.len() <= row {
|
||||||
|
|
@ -3703,6 +3770,7 @@ impl Tab {
|
||||||
.padding(space_xxxs)
|
.padding(space_xxxs)
|
||||||
.class(button_style(
|
.class(button_style(
|
||||||
item.selected,
|
item.selected,
|
||||||
|
item.highlighted,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
|
@ -3711,7 +3779,13 @@ impl Tab {
|
||||||
.id(item.button_id.clone())
|
.id(item.button_id.clone())
|
||||||
.on_press(Message::Click(Some(*i)))
|
.on_press(Message::Click(Some(*i)))
|
||||||
.padding([0, space_xxxs])
|
.padding([0, space_xxxs])
|
||||||
.class(button_style(item.selected, true, true, false)),
|
.class(button_style(
|
||||||
|
item.selected,
|
||||||
|
item.highlighted,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
)),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut column = widget::column::with_capacity(buttons.len())
|
let mut column = widget::column::with_capacity(buttons.len())
|
||||||
|
|
@ -3922,12 +3996,20 @@ impl Tab {
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.id(item.button_id.clone())
|
.id(item.button_id.clone())
|
||||||
.padding([0, space_xxs])
|
.padding([0, space_xxs])
|
||||||
.class(button_style(item.selected, true, false, false)),
|
.class(button_style(
|
||||||
|
item.selected,
|
||||||
|
item.highlighted,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
.on_press(move |_| Message::Click(Some(i)))
|
.on_press(move |_| Message::Click(Some(i)))
|
||||||
.on_double_click(move |_| Message::DoubleClick(Some(i)))
|
.on_double_click(move |_| Message::DoubleClick(Some(i)))
|
||||||
.on_release(move |_| Message::ClickRelease(Some(i)))
|
.on_release(move |_| Message::ClickRelease(Some(i)))
|
||||||
.on_middle_press(move |_| Message::MiddleClick(i));
|
.on_middle_press(move |_| Message::MiddleClick(i))
|
||||||
|
.on_enter(move || Message::HighlightActivate(i))
|
||||||
|
.on_exit(move || Message::HighlightDeactivate(i));
|
||||||
|
|
||||||
if self.context_menu.is_some() {
|
if self.context_menu.is_some() {
|
||||||
mouse_area
|
mouse_area
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue