WIP: buildout mouse interactions
This commit is contained in:
parent
61fe3c093d
commit
c8f4eb9d34
4 changed files with 224 additions and 136 deletions
57
src/app.rs
57
src/app.rs
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
184
src/tab.rs
184
src/tab.rs
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue