WIP: buildout mouse interactions

This commit is contained in:
Jeremy Soller 2024-02-26 12:05:29 -07:00
parent 61fe3c093d
commit c8f4eb9d34
4 changed files with 224 additions and 136 deletions

View file

@ -10,7 +10,7 @@ use cosmic::{
futures::{self, SinkExt}, futures::{self, SinkExt},
keyboard::{Event as KeyEvent, Key, Modifiers}, keyboard::{Event as KeyEvent, Key, Modifiers},
subscription::{self, Subscription}, subscription::{self, Subscription},
window, Event, Length, Point, window, Event, Length,
}, },
style, style,
widget::{self, segmented_button}, widget::{self, segmented_button},
@ -29,7 +29,7 @@ use crate::{
config::{AppTheme, Config, IconSizes, TabConfig, CONFIG_VERSION}, config::{AppTheme, Config, IconSizes, TabConfig, CONFIG_VERSION},
fl, home_dir, fl, home_dir,
key_bind::{key_binds, KeyBind}, key_bind::{key_binds, KeyBind},
menu, mouse_area, menu,
operation::Operation, operation::Operation,
tab::{self, ItemMetadata, Location, Tab}, tab::{self, ItemMetadata, Location, Tab},
}; };
@ -126,7 +126,6 @@ pub enum Message {
TabClose(Option<segmented_button::Entity>), TabClose(Option<segmented_button::Entity>),
TabConfig(TabConfig), TabConfig(TabConfig),
TabContextAction(segmented_button::Entity, Action), TabContextAction(segmented_button::Entity, Action),
TabContextMenu(segmented_button::Entity, Option<Point>),
TabMessage(Option<segmented_button::Entity>, tab::Message), TabMessage(Option<segmented_button::Entity>, tab::Message),
TabNew, TabNew,
TabRescan(segmented_button::Entity, Vec<tab::Item>), TabRescan(segmented_button::Entity, Vec<tab::Item>),
@ -862,20 +861,14 @@ impl Application for App {
_ => {} _ => {}
} }
} }
Message::TabContextMenu(entity, position_opt) => {
match self.tab_model.data_mut::<Tab>(entity) {
Some(tab) => {
// Update context menu position
tab.context_menu = position_opt;
}
_ => {}
}
// Disable side context page
self.core.window.show_context = false;
}
Message::TabMessage(entity_opt, tab_message) => { Message::TabMessage(entity_opt, tab_message) => {
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
if let tab::Message::ContextMenu(_point_opt) = tab_message {
// Disable side context page
self.core.window.show_context = false;
}
let mut update_opt = None; let mut update_opt = None;
match self.tab_model.data_mut::<Tab>(entity) { match self.tab_model.data_mut::<Tab>(entity) {
Some(tab) => { Some(tab) => {
@ -981,38 +974,10 @@ impl Application for App {
let entity = self.tab_model.active(); let entity = self.tab_model.active();
match self.tab_model.data::<Tab>(entity) { match self.tab_model.data::<Tab>(entity) {
Some(tab) => { Some(tab) => {
let mut mouse_area = mouse_area::MouseArea::new( let tab_view = tab
tab.view(self.core()) .view(self.core())
.map(move |message| Message::TabMessage(Some(entity), message)), .map(move |message| Message::TabMessage(Some(entity), message));
) tab_column = tab_column.push(tab_view);
.on_press(move |_point_opt| {
Message::TabMessage(Some(entity), tab::Message::Click(None))
})
.on_back_press(move |_point_opt| {
Message::TabMessage(None, tab::Message::GoPrevious)
})
.on_forward_press(move |_point_opt| {
Message::TabMessage(None, tab::Message::GoNext)
});
if tab.context_menu.is_some() {
mouse_area = mouse_area
.on_right_press(move |_point_opt| Message::TabContextMenu(entity, None));
} else {
mouse_area = mouse_area.on_right_press(move |point_opt| {
Message::TabContextMenu(entity, point_opt)
});
}
let mut popover = widget::popover(mouse_area, menu::context_menu(entity, &tab));
match tab.context_menu {
Some(point) => {
let rounded = Point::new(point.x.round(), point.y.round());
popover = popover.position(rounded);
}
None => {
popover = popover.show_popup(false);
}
}
tab_column = tab_column.push(popover);
} }
None => { None => {
//TODO //TODO

View file

@ -7,7 +7,6 @@ use cosmic::{
widget::{ widget::{
self, self,
menu::{ItemHeight, ItemWidth, MenuBar, MenuTree}, menu::{ItemHeight, ItemWidth, MenuBar, MenuTree},
segmented_button,
}, },
Element, Element,
}; };
@ -17,7 +16,7 @@ use crate::{
app::{Action, Message}, app::{Action, Message},
fl, fl,
key_bind::KeyBind, key_bind::KeyBind,
tab::{Location, Tab}, tab::{self, Location, Tab},
}; };
macro_rules! menu_button { macro_rules! menu_button {
@ -35,10 +34,10 @@ macro_rules! menu_button {
); );
} }
pub fn context_menu<'a>(entity: segmented_button::Entity, tab: &Tab) -> Element<'a, Message> { pub fn context_menu<'a>(tab: &Tab) -> Element<'a, tab::Message> {
//TODO: show key bindings in context menu? //TODO: show key bindings in context menu?
let menu_action = |label, action| { let menu_action = |label, action| {
menu_button!(widget::text(label)).on_press(Message::TabContextAction(entity, action)) menu_button!(widget::text(label)).on_press(tab::Message::ContextAction(action))
}; };
let selected = tab let selected = tab

View file

@ -1,16 +1,22 @@
//! A container for capturing mouse events. //! A container for capturing mouse events.
use cosmic::iced_core::{ use cosmic::{
event::{self, Event}, iced_core::{
layout, mouse, overlay, renderer, touch, border::Border,
widget::{tree, Operation, OperationOutputWrapper, Tree}, event::{self, Event},
Size, {Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Widget}, layout, mouse, overlay,
renderer::{self, Quad, Renderer as _},
touch,
widget::{tree, Operation, OperationOutputWrapper, Tree},
Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Size, Widget,
},
Element, Renderer, Theme,
}; };
/// Emit messages on mouse events. /// Emit messages on mouse events.
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct MouseArea<'a, Message, Theme, Renderer> { pub struct MouseArea<'a, Message> {
content: Element<'a, Message, Theme, Renderer>, content: Element<'a, Message>,
on_drag: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>, on_drag: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
on_press: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>, on_press: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
on_release: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>, on_release: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
@ -23,9 +29,10 @@ pub struct MouseArea<'a, Message, Theme, Renderer> {
on_back_release: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>, on_back_release: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
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>>,
show_drag_box: bool,
} }
impl<'a, Message, Theme, Renderer> MouseArea<'a, Message, Theme, Renderer> { impl<'a, Message> MouseArea<'a, Message> {
/// The message to emit when a drag is initiated. /// The message to emit when a drag is initiated.
#[must_use] #[must_use]
pub fn on_drag(mut self, message: impl Fn(Option<Point>) -> Message + 'a) -> Self { pub fn on_drag(mut self, message: impl Fn(Option<Point>) -> Message + 'a) -> Self {
@ -112,6 +119,12 @@ impl<'a, Message, Theme, Renderer> MouseArea<'a, Message, Theme, Renderer> {
self.on_forward_release = Some(Box::new(message)); self.on_forward_release = Some(Box::new(message));
self self
} }
#[must_use]
pub fn show_drag_box(mut self, show_drag_box: bool) -> Self {
self.show_drag_box = show_drag_box;
self
}
} }
/// Local state of the [`MouseArea`]. /// Local state of the [`MouseArea`].
@ -121,9 +134,9 @@ struct State {
drag_initiated: Option<Point>, drag_initiated: Option<Point>,
} }
impl<'a, Message, Theme, Renderer> MouseArea<'a, Message, Theme, Renderer> { impl<'a, Message> MouseArea<'a, Message> {
/// Creates a [`MouseArea`] with the given content. /// Creates a [`MouseArea`] with the given content.
pub fn new(content: impl Into<Element<'a, Message, Theme, Renderer>>) -> Self { pub fn new(content: impl Into<Element<'a, Message>>) -> Self {
MouseArea { MouseArea {
content: content.into(), content: content.into(),
on_drag: None, on_drag: None,
@ -138,14 +151,13 @@ impl<'a, Message, Theme, Renderer> MouseArea<'a, Message, Theme, Renderer> {
on_back_release: None, on_back_release: None,
on_forward_press: None, on_forward_press: None,
on_forward_release: None, on_forward_release: None,
show_drag_box: false,
} }
} }
} }
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer> impl<'a, Message> Widget<Message, Theme, Renderer> for MouseArea<'a, Message>
for MouseArea<'a, Message, Theme, Renderer>
where where
Renderer: renderer::Renderer,
Message: Clone, Message: Clone,
{ {
fn tag(&self) -> tree::Tag { fn tag(&self) -> tree::Tag {
@ -261,6 +273,38 @@ where
cursor, cursor,
viewport, viewport,
); );
if self.show_drag_box {
let state = tree.state.downcast_ref::<State>();
if let Some(a) = state.drag_initiated {
if let Some(b) = cursor.position() {
let min_x = a.x.min(b.x);
let max_x = a.x.max(b.x);
let min_y = a.y.min(b.y);
let max_y = a.y.max(b.y);
let bounds = Rectangle::new(
Point::new(min_x, min_y),
Size::new(max_x - min_x, max_y - min_y),
);
let cosmic = theme.cosmic();
let mut bg_color = cosmic.accent_color();
//TODO: get correct alpha
bg_color.alpha = 0.2;
renderer.fill_quad(
Quad {
bounds,
border: Border {
color: cosmic.accent_color().into(),
width: 1.0,
radius: cosmic.radius_xs().into(),
},
..Default::default()
},
Color::from(bg_color),
);
}
}
}
} }
fn overlay<'b>( fn overlay<'b>(
@ -275,50 +319,47 @@ where
} }
} }
impl<'a, Message, Theme, Renderer> From<MouseArea<'a, Message, Theme, Renderer>> impl<'a, Message> From<MouseArea<'a, Message>> for Element<'a, Message>
for Element<'a, Message, Theme, Renderer>
where where
Message: 'a + Clone, Message: 'a + Clone,
Renderer: 'a + renderer::Renderer, Renderer: 'a + renderer::Renderer,
Theme: 'a, Theme: 'a,
{ {
fn from( fn from(area: MouseArea<'a, Message>) -> Element<'a, Message> {
area: MouseArea<'a, Message, Theme, Renderer>,
) -> Element<'a, Message, Theme, Renderer> {
Element::new(area) Element::new(area)
} }
} }
/// Processes the given [`Event`] and updates the [`State`] of an [`MouseArea`] /// Processes the given [`Event`] and updates the [`State`] of an [`MouseArea`]
/// accordingly. /// accordingly.
fn update<Message: Clone, Theme, Renderer>( fn update<Message: Clone>(
widget: &mut MouseArea<'_, Message, Theme, Renderer>, widget: &mut MouseArea<'_, Message>,
event: &Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
state: &mut State, state: &mut State,
) -> event::Status { ) -> event::Status {
if !cursor.is_over(layout.bounds()) { if state.drag_initiated.is_none() && !cursor.is_over(layout.bounds()) {
return event::Status::Ignored; return event::Status::Ignored;
} }
if let Some(message) = widget.on_press.as_ref() { if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) = event
| Event::Touch(touch::Event::FingerPressed { .. }) = event {
{ state.drag_initiated = cursor.position();
state.drag_initiated = cursor.position(); if let Some(message) = widget.on_press.as_ref() {
shell.publish(message(cursor.position_in(layout.bounds()))); shell.publish(message(cursor.position_in(layout.bounds())));
return event::Status::Captured; return event::Status::Captured;
} }
} }
if let Some(message) = widget.on_release.as_ref() { if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) | Event::Touch(touch::Event::FingerLifted { .. }) = event
| Event::Touch(touch::Event::FingerLifted { .. }) = event {
{ state.drag_initiated = None;
state.drag_initiated = None; if let Some(message) = widget.on_release.as_ref() {
shell.publish(message(cursor.position_in(layout.bounds()))); shell.publish(message(cursor.position_in(layout.bounds())));
return event::Status::Captured; return event::Status::Captured;
@ -397,16 +438,9 @@ fn update<Message: Clone, Theme, Renderer>(
} }
} }
if state.drag_initiated.is_none() && widget.on_drag.is_some() { if let Some((message, drag_source)) = widget.on_drag.as_ref().zip(state.drag_initiated) {
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
| Event::Touch(touch::Event::FingerPressed { .. }) = event
{
state.drag_initiated = cursor.position();
}
} else if let Some((message, drag_source)) = widget.on_drag.as_ref().zip(state.drag_initiated) {
if let Some(position) = cursor.position() { if let Some(position) = cursor.position() {
if position.distance(drag_source) > 1.0 { if position.distance(drag_source) > 1.0 {
state.drag_initiated = None;
shell.publish(message(cursor.position_in(layout.bounds()))); shell.publish(message(cursor.position_in(layout.bounds())));
return event::Status::Captured; return event::Status::Captured;

View file

@ -7,11 +7,13 @@ use cosmic::{
keyboard::Modifiers, keyboard::Modifiers,
subscription::{self, Subscription}, subscription::{self, Subscription},
//TODO: export in cosmic::widget //TODO: export in cosmic::widget
widget::horizontal_rule, widget::{horizontal_rule, scrollable::Viewport},
Alignment, Alignment,
ContentFit, ContentFit,
Length, Length,
Point, Point,
Rectangle,
Size,
}, },
theme, widget, Element, theme, widget, Element,
}; };
@ -28,10 +30,12 @@ use std::{
}; };
use crate::{ use crate::{
app::Action,
config::{IconSizes, TabConfig}, config::{IconSizes, TabConfig},
dialog::DialogKind, dialog::DialogKind,
fl, fl, menu,
mime_icon::mime_icon, mime_icon::mime_icon,
mouse_area,
}; };
const DOUBLE_CLICK_DURATION: Duration = Duration::from_millis(500); const DOUBLE_CLICK_DURATION: Duration = Duration::from_millis(500);
@ -390,12 +394,16 @@ impl Location {
pub enum Message { pub enum Message {
Click(Option<usize>), Click(Option<usize>),
Config(TabConfig), Config(TabConfig),
ContextAction(Action),
ContextMenu(Option<Point>),
Drag(Option<Point>),
EditLocation(Option<Location>), EditLocation(Option<Location>),
GoNext, GoNext,
GoPrevious, GoPrevious,
Location(Location), Location(Location),
LocationUp, LocationUp,
RightClick(usize), RightClick(usize),
Scroll(Viewport),
Thumbnail(PathBuf, Result<image::RgbaImage, ()>), Thumbnail(PathBuf, Result<image::RgbaImage, ()>),
ToggleShowHidden, ToggleShowHidden,
View(View), View(View),
@ -542,6 +550,8 @@ pub struct Tab {
pub items_opt: Option<Vec<Item>>, pub items_opt: Option<Vec<Item>>,
pub view: View, pub view: View,
pub dialog: Option<DialogKind>, pub dialog: Option<DialogKind>,
pub drag_opt: Option<Point>,
pub scroll_opt: Option<Viewport>,
pub edit_location: Option<Location>, pub edit_location: Option<Location>,
pub history_i: usize, pub history_i: usize,
pub history: Vec<Location>, pub history: Vec<Location>,
@ -555,8 +565,10 @@ impl Tab {
location, location,
context_menu: None, context_menu: None,
items_opt: None, items_opt: None,
view: View::List, view: View::Grid,
dialog: None, dialog: None,
drag_opt: None,
scroll_opt: None,
edit_location: None, edit_location: None,
history_i: 0, history_i: 0,
history, history,
@ -576,6 +588,23 @@ impl Tab {
} }
} }
fn select_by_drag(&mut self, rect: Rectangle) {
let items = match &mut self.items_opt {
Some(some) => some,
None => return,
};
println!("{:?}", rect);
let (row, col) = match self.view {
View::Grid => (0, 0),
View::List => (0, 0),
};
for (i, item) in items.iter_mut().enumerate() {
item.selected = false;
//TODO
}
}
pub fn update(&mut self, message: Message, modifiers: Modifiers) -> bool { pub fn update(&mut self, message: Message, modifiers: Modifiers) -> bool {
let mut cd = None; let mut cd = None;
let mut history_i_opt = None; let mut history_i_opt = None;
@ -629,6 +658,43 @@ impl Tab {
Message::Config(config) => { Message::Config(config) => {
self.config = config; self.config = config;
} }
Message::ContextAction(action) => {
// Close context menu
self.context_menu = None;
// TODO: run actions message
println!("TODO {:?}", action);
}
Message::ContextMenu(point_opt) => {
self.context_menu = point_opt;
}
Message::Drag(point_opt) => match point_opt {
Some(point) => {
let drag = match self.drag_opt {
Some(some) => some,
None => {
self.drag_opt = Some(point);
point
}
};
let min_x = drag.x.min(point.x);
let max_x = drag.x.max(point.x);
let min_y = drag.y.min(point.y);
let max_y = drag.y.max(point.y);
let offset_y = self
.scroll_opt
.map(|x| x.absolute_offset().y)
.unwrap_or_default();
let rect = Rectangle::new(
Point::new(min_x, min_y + offset_y),
Size::new(max_x - min_x, max_y - min_y),
);
self.select_by_drag(rect);
}
None => {
self.drag_opt = None;
}
},
Message::EditLocation(edit_location) => { Message::EditLocation(edit_location) => {
self.edit_location = edit_location; self.edit_location = edit_location;
} }
@ -679,6 +745,9 @@ impl Tab {
} }
} }
} }
Message::Scroll(viewport) => {
self.scroll_opt = Some(viewport);
}
Message::Thumbnail(path, thumbnail_res) => { Message::Thumbnail(path, thumbnail_res) => {
if let Some(ref mut items) = self.items_opt { if let Some(ref mut items) = self.items_opt {
for item in items.iter_mut() { for item in items.iter_mut() {
@ -875,30 +944,27 @@ impl Tab {
pub fn empty_view(&self, has_hidden: bool, core: &Core) -> Element<Message> { pub fn empty_view(&self, has_hidden: bool, core: &Core) -> Element<Message> {
let cosmic_theme::Spacing { space_xxs, .. } = core.system_theme().cosmic().spacing; let cosmic_theme::Spacing { space_xxs, .. } = core.system_theme().cosmic().spacing;
widget::column::with_children(vec![ widget::column::with_children(vec![widget::container(
self.location_view(core), widget::column::with_children(vec![
widget::container( widget::icon::from_name("folder-symbolic")
widget::column::with_children(vec![ .size(64)
widget::icon::from_name("folder-symbolic") .icon()
.size(64)
.icon()
.into(),
widget::text(if has_hidden {
fl!("empty-folder-hidden")
} else {
fl!("empty-folder")
})
.into(), .into(),
]) widget::text(if has_hidden {
.align_items(Alignment::Center) fl!("empty-folder-hidden")
.spacing(space_xxs), } else {
) fl!("empty-folder")
.align_x(Horizontal::Center) })
.align_y(Vertical::Center) .into(),
.width(Length::Fill) ])
.height(Length::Fill) .align_items(Alignment::Center)
.into(), .spacing(space_xxs),
]) )
.align_x(Horizontal::Center)
.align_y(Vertical::Center)
.width(Length::Fill)
.height(Length::Fill)
.into()])
.into() .into()
} }
@ -936,13 +1002,14 @@ impl Tab {
.height(item_height) .height(item_height)
.width(item_width), .width(item_width),
) )
.padding(0)
.style(button_style(item.selected)) .style(button_style(item.selected))
.on_press(Message::Click(Some(i))); .on_press(Message::Click(Some(i)));
if self.context_menu.is_some() { if self.context_menu.is_some() {
children.push(button.into()); children.push(button.into());
} else { } else {
children.push( children.push(
crate::mouse_area::MouseArea::new(button) mouse_area::MouseArea::new(button)
.on_right_press_no_capture(move |_point_opt| Message::RightClick(i)) .on_right_press_no_capture(move |_point_opt| Message::RightClick(i))
.into(), .into(),
); );
@ -954,13 +1021,10 @@ impl Tab {
return self.empty_view(hidden > 0, core); return self.empty_view(hidden > 0, core);
} }
} }
widget::column::with_children(vec![ widget::scrollable(widget::flex_row(children))
self.location_view(core), .on_scroll(Message::Scroll)
widget::scrollable(widget::flex_row(children)) .width(Length::Fill)
.width(Length::Fill) .into()
.into(),
])
.into()
} }
pub fn list_view(&self, core: &Core) -> Element<Message> { pub fn list_view(&self, core: &Core) -> Element<Message> {
@ -1060,7 +1124,7 @@ impl Tab {
children.push(button.into()); children.push(button.into());
} else { } else {
children.push( children.push(
crate::mouse_area::MouseArea::new(button) mouse_area::MouseArea::new(button)
.on_right_press_no_capture(move |_point_opt| Message::RightClick(i)) .on_right_press_no_capture(move |_point_opt| Message::RightClick(i))
.into(), .into(),
); );
@ -1073,24 +1137,50 @@ impl Tab {
} }
} }
widget::column::with_children(vec![ widget::scrollable(
self.location_view(core).into(), widget::column::with_children(children)
widget::scrollable( // Hack to make room for scroll bar
widget::column::with_children(children) .padding([0, space_xxs, 0, 0]),
// Hack to make room for scroll bar )
.padding([0, space_xxs, 0, 0]), .on_scroll(Message::Scroll)
) .width(Length::Fill)
.width(Length::Fill)
.into(),
])
.into() .into()
} }
pub fn view(&self, core: &Core) -> Element<Message> { pub fn view(&self, core: &Core) -> Element<Message> {
widget::container(match self.view { let location_view = self.location_view(core);
let item_view = match self.view {
View::Grid => self.grid_view(core), View::Grid => self.grid_view(core),
View::List => self.list_view(core), View::List => self.list_view(core),
}) };
let mut mouse_area =
mouse_area::MouseArea::new(widget::container(item_view).height(Length::Fill))
.on_drag(move |point_opt| Message::Drag(point_opt))
.on_press(move |_point_opt| Message::Click(None))
.on_release(move |point_opt| Message::Drag(None))
.on_back_press(move |_point_opt| Message::GoPrevious)
.on_forward_press(move |_point_opt| Message::GoNext)
.show_drag_box(true);
if self.context_menu.is_some() {
mouse_area = mouse_area.on_right_press(move |_point_opt| Message::ContextMenu(None));
} else {
mouse_area =
mouse_area.on_right_press(move |point_opt| Message::ContextMenu(point_opt));
}
let mut popover = widget::popover(mouse_area, menu::context_menu(&self));
match self.context_menu {
Some(point) => {
let rounded = Point::new(point.x.round(), point.y.round());
popover = popover.position(rounded);
}
None => {
popover = popover.show_popup(false);
}
}
widget::container(widget::column::with_children(vec![
location_view,
popover.into(),
]))
.height(Length::Fill) .height(Length::Fill)
.width(Length::Fill) .width(Length::Fill)
.into() .into()