Merge branch 'master' into sentinel
This commit is contained in:
commit
caecbf20ef
12 changed files with 257 additions and 249 deletions
|
|
@ -48,12 +48,21 @@ impl Length {
|
||||||
/// Specifically:
|
/// Specifically:
|
||||||
/// - [`Length::Shrink`] if [`Length::Shrink`] or [`Length::Fixed`].
|
/// - [`Length::Shrink`] if [`Length::Shrink`] or [`Length::Fixed`].
|
||||||
/// - [`Length::Fill`] otherwise.
|
/// - [`Length::Fill`] otherwise.
|
||||||
pub fn fluid(&self) -> Length {
|
pub fn fluid(&self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Length::Fill | Length::FillPortion(_) => Length::Fill,
|
Length::Fill | Length::FillPortion(_) => Length::Fill,
|
||||||
Length::Shrink | Length::Fixed(_) => Length::Shrink,
|
Length::Shrink | Length::Fixed(_) => Length::Shrink,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adapts the [`Length`] so it can contain the other [`Length`] and
|
||||||
|
/// match its fluidity.
|
||||||
|
pub fn enclose(self, other: Length) -> Self {
|
||||||
|
match (self, other) {
|
||||||
|
(Length::Shrink, Length::Fill | Length::FillPortion(_)) => other,
|
||||||
|
_ => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Pixels> for Length {
|
impl From<Pixels> for Length {
|
||||||
|
|
|
||||||
|
|
@ -115,15 +115,10 @@ where
|
||||||
child: impl Into<Element<'a, Message, Theme, Renderer>>,
|
child: impl Into<Element<'a, Message, Theme, Renderer>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let child = child.into();
|
let child = child.into();
|
||||||
let size = child.as_widget().size_hint();
|
let child_size = child.as_widget().size_hint();
|
||||||
|
|
||||||
if size.width.is_fill() {
|
self.width = self.width.enclose(child_size.width);
|
||||||
self.width = Length::Fill;
|
self.height = self.height.enclose(child_size.height);
|
||||||
}
|
|
||||||
|
|
||||||
if size.height.is_fill() {
|
|
||||||
self.height = Length::Fill;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.children.push(child);
|
self.children.push(child);
|
||||||
self
|
self
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use crate::rule::{self, Rule};
|
||||||
use crate::runtime::Command;
|
use crate::runtime::Command;
|
||||||
use crate::scrollable::{self, Scrollable};
|
use crate::scrollable::{self, Scrollable};
|
||||||
use crate::slider::{self, Slider};
|
use crate::slider::{self, Slider};
|
||||||
|
use crate::style::application;
|
||||||
use crate::text::{self, Text};
|
use crate::text::{self, Text};
|
||||||
use crate::text_editor::{self, TextEditor};
|
use crate::text_editor::{self, TextEditor};
|
||||||
use crate::text_input::{self, TextInput};
|
use crate::text_input::{self, TextInput};
|
||||||
|
|
@ -445,6 +446,7 @@ pub fn themer<'a, Message, Theme, Renderer>(
|
||||||
) -> Themer<'a, Message, Theme, Renderer>
|
) -> Themer<'a, Message, Theme, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: core::Renderer,
|
Renderer: core::Renderer,
|
||||||
|
Theme: application::StyleSheet,
|
||||||
{
|
{
|
||||||
Themer::new(theme, content)
|
Themer::new(theme, content)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,15 +110,10 @@ where
|
||||||
child: impl Into<Element<'a, Message, Theme, Renderer>>,
|
child: impl Into<Element<'a, Message, Theme, Renderer>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let child = child.into();
|
let child = child.into();
|
||||||
let size = child.as_widget().size_hint();
|
let child_size = child.as_widget().size_hint();
|
||||||
|
|
||||||
if size.width.is_fill() {
|
self.width = self.width.enclose(child_size.width);
|
||||||
self.width = Length::Fill;
|
self.height = self.height.enclose(child_size.height);
|
||||||
}
|
|
||||||
|
|
||||||
if size.height.is_fill() {
|
|
||||||
self.height = Length::Fill;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.keys.push(key);
|
self.keys.push(key);
|
||||||
self.children.push(child);
|
self.children.push(child);
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,9 @@ use crate::core::{
|
||||||
Vector, Widget,
|
Vector, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DRAG_DEADBAND_DISTANCE: f32 = 10.0;
|
||||||
|
const THICKNESS_RATIO: f32 = 25.0;
|
||||||
|
|
||||||
/// A collection of panes distributed using either vertical or horizontal splits
|
/// A collection of panes distributed using either vertical or horizontal splits
|
||||||
/// to completely fill the space available.
|
/// to completely fill the space available.
|
||||||
///
|
///
|
||||||
|
|
@ -532,8 +535,6 @@ pub fn update<'a, Message, T: Draggable>(
|
||||||
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
|
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
|
||||||
on_resize: &Option<(f32, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>,
|
on_resize: &Option<(f32, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>,
|
||||||
) -> event::Status {
|
) -> event::Status {
|
||||||
const DRAG_DEADBAND_DISTANCE: f32 = 10.0;
|
|
||||||
|
|
||||||
let mut event_status = event::Status::Ignored;
|
let mut event_status = event::Status::Ignored;
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
|
|
@ -575,6 +576,7 @@ pub fn update<'a, Message, T: Draggable>(
|
||||||
shell,
|
shell,
|
||||||
contents,
|
contents,
|
||||||
on_click,
|
on_click,
|
||||||
|
on_drag,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -586,6 +588,7 @@ pub fn update<'a, Message, T: Draggable>(
|
||||||
shell,
|
shell,
|
||||||
contents,
|
contents,
|
||||||
on_click,
|
on_click,
|
||||||
|
on_drag,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -594,38 +597,44 @@ pub fn update<'a, Message, T: Draggable>(
|
||||||
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
|
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
|
||||||
| Event::Touch(touch::Event::FingerLifted { .. })
|
| Event::Touch(touch::Event::FingerLifted { .. })
|
||||||
| Event::Touch(touch::Event::FingerLost { .. }) => {
|
| Event::Touch(touch::Event::FingerLost { .. }) => {
|
||||||
if let Some((pane, _)) = action.picked_pane() {
|
if let Some((pane, origin)) = action.picked_pane() {
|
||||||
if let Some(on_drag) = on_drag {
|
if let Some(on_drag) = on_drag {
|
||||||
if let Some(cursor_position) = cursor.position() {
|
if let Some(cursor_position) = cursor.position() {
|
||||||
let event = if let Some(edge) =
|
if cursor_position.distance(origin)
|
||||||
in_edge(layout, cursor_position)
|
> DRAG_DEADBAND_DISTANCE
|
||||||
{
|
{
|
||||||
DragEvent::Dropped {
|
let event = if let Some(edge) =
|
||||||
pane,
|
in_edge(layout, cursor_position)
|
||||||
target: Target::Edge(edge),
|
{
|
||||||
}
|
DragEvent::Dropped {
|
||||||
} else {
|
pane,
|
||||||
let dropped_region = contents
|
target: Target::Edge(edge),
|
||||||
.zip(layout.children())
|
|
||||||
.find_map(|(target, layout)| {
|
|
||||||
layout_region(layout, cursor_position)
|
|
||||||
.map(|region| (target, region))
|
|
||||||
});
|
|
||||||
|
|
||||||
match dropped_region {
|
|
||||||
Some(((target, _), region))
|
|
||||||
if pane != target =>
|
|
||||||
{
|
|
||||||
DragEvent::Dropped {
|
|
||||||
pane,
|
|
||||||
target: Target::Pane(target, region),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => DragEvent::Canceled { pane },
|
} else {
|
||||||
}
|
let dropped_region = contents
|
||||||
};
|
.zip(layout.children())
|
||||||
|
.find_map(|(target, layout)| {
|
||||||
|
layout_region(layout, cursor_position)
|
||||||
|
.map(|region| (target, region))
|
||||||
|
});
|
||||||
|
|
||||||
shell.publish(on_drag(event));
|
match dropped_region {
|
||||||
|
Some(((target, _), region))
|
||||||
|
if pane != target =>
|
||||||
|
{
|
||||||
|
DragEvent::Dropped {
|
||||||
|
pane,
|
||||||
|
target: Target::Pane(
|
||||||
|
target, region,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => DragEvent::Canceled { pane },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
shell.publish(on_drag(event));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -638,49 +647,7 @@ pub fn update<'a, Message, T: Draggable>(
|
||||||
}
|
}
|
||||||
Event::Mouse(mouse::Event::CursorMoved { .. })
|
Event::Mouse(mouse::Event::CursorMoved { .. })
|
||||||
| Event::Touch(touch::Event::FingerMoved { .. }) => {
|
| Event::Touch(touch::Event::FingerMoved { .. }) => {
|
||||||
if let Some((_, origin)) = action.clicked_pane() {
|
if let Some((_, on_resize)) = on_resize {
|
||||||
if let Some(on_drag) = &on_drag {
|
|
||||||
let bounds = layout.bounds();
|
|
||||||
|
|
||||||
if let Some(cursor_position) = cursor.position_over(bounds)
|
|
||||||
{
|
|
||||||
let mut clicked_region = contents
|
|
||||||
.zip(layout.children())
|
|
||||||
.filter(|(_, layout)| {
|
|
||||||
layout.bounds().contains(cursor_position)
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(((pane, content), layout)) =
|
|
||||||
clicked_region.next()
|
|
||||||
{
|
|
||||||
if content
|
|
||||||
.can_be_dragged_at(layout, cursor_position)
|
|
||||||
{
|
|
||||||
let pane_position = layout.position();
|
|
||||||
|
|
||||||
let new_origin = cursor_position
|
|
||||||
- Vector::new(
|
|
||||||
pane_position.x,
|
|
||||||
pane_position.y,
|
|
||||||
);
|
|
||||||
|
|
||||||
if new_origin.distance(origin)
|
|
||||||
> DRAG_DEADBAND_DISTANCE
|
|
||||||
{
|
|
||||||
*action = state::Action::Dragging {
|
|
||||||
pane,
|
|
||||||
origin,
|
|
||||||
};
|
|
||||||
|
|
||||||
shell.publish(on_drag(DragEvent::Picked {
|
|
||||||
pane,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let Some((_, on_resize)) = on_resize {
|
|
||||||
if let Some((split, _)) = action.picked_split() {
|
if let Some((split, _)) = action.picked_split() {
|
||||||
let bounds = layout.bounds();
|
let bounds = layout.bounds();
|
||||||
|
|
||||||
|
|
@ -755,6 +722,7 @@ fn click_pane<'a, Message, T>(
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
contents: impl Iterator<Item = (Pane, T)>,
|
contents: impl Iterator<Item = (Pane, T)>,
|
||||||
on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>,
|
on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>,
|
||||||
|
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
|
||||||
) where
|
) where
|
||||||
T: Draggable,
|
T: Draggable,
|
||||||
{
|
{
|
||||||
|
|
@ -762,15 +730,21 @@ fn click_pane<'a, Message, T>(
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
.filter(|(_, layout)| layout.bounds().contains(cursor_position));
|
.filter(|(_, layout)| layout.bounds().contains(cursor_position));
|
||||||
|
|
||||||
if let Some(((pane, _), layout)) = clicked_region.next() {
|
if let Some(((pane, content), layout)) = clicked_region.next() {
|
||||||
if let Some(on_click) = &on_click {
|
if let Some(on_click) = &on_click {
|
||||||
shell.publish(on_click(pane));
|
shell.publish(on_click(pane));
|
||||||
}
|
}
|
||||||
|
|
||||||
let pane_position = layout.position();
|
if let Some(on_drag) = &on_drag {
|
||||||
let origin =
|
if content.can_be_dragged_at(layout, cursor_position) {
|
||||||
cursor_position - Vector::new(pane_position.x, pane_position.y);
|
*action = state::Action::Dragging {
|
||||||
*action = state::Action::Clicking { pane, origin };
|
pane,
|
||||||
|
origin: cursor_position,
|
||||||
|
};
|
||||||
|
|
||||||
|
shell.publish(on_drag(DragEvent::Picked { pane }));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -783,7 +757,7 @@ pub fn mouse_interaction(
|
||||||
spacing: f32,
|
spacing: f32,
|
||||||
resize_leeway: Option<f32>,
|
resize_leeway: Option<f32>,
|
||||||
) -> Option<mouse::Interaction> {
|
) -> Option<mouse::Interaction> {
|
||||||
if action.clicked_pane().is_some() || action.picked_pane().is_some() {
|
if action.picked_pane().is_some() {
|
||||||
return Some(mouse::Interaction::Grabbing);
|
return Some(mouse::Interaction::Grabbing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -841,7 +815,13 @@ pub fn draw<Theme, Renderer, T>(
|
||||||
Theme: StyleSheet,
|
Theme: StyleSheet,
|
||||||
Renderer: crate::core::Renderer,
|
Renderer: crate::core::Renderer,
|
||||||
{
|
{
|
||||||
let picked_pane = action.picked_pane();
|
let picked_pane = action.picked_pane().filter(|(_, origin)| {
|
||||||
|
cursor
|
||||||
|
.position()
|
||||||
|
.map(|position| position.distance(*origin))
|
||||||
|
.unwrap_or_default()
|
||||||
|
> DRAG_DEADBAND_DISTANCE
|
||||||
|
});
|
||||||
|
|
||||||
let picked_split = action
|
let picked_split = action
|
||||||
.picked_split()
|
.picked_split()
|
||||||
|
|
@ -962,8 +942,7 @@ pub fn draw<Theme, Renderer, T>(
|
||||||
if let Some(cursor_position) = cursor.position() {
|
if let Some(cursor_position) = cursor.position() {
|
||||||
let bounds = layout.bounds();
|
let bounds = layout.bounds();
|
||||||
|
|
||||||
let translation = cursor_position
|
let translation = cursor_position - Point::new(origin.x, origin.y);
|
||||||
- Point::new(bounds.x + origin.x, bounds.y + origin.y);
|
|
||||||
|
|
||||||
renderer.with_translation(translation, |renderer| {
|
renderer.with_translation(translation, |renderer| {
|
||||||
renderer.with_layer(bounds, |renderer| {
|
renderer.with_layer(bounds, |renderer| {
|
||||||
|
|
@ -1020,8 +999,6 @@ pub fn draw<Theme, Renderer, T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const THICKNESS_RATIO: f32 = 25.0;
|
|
||||||
|
|
||||||
fn in_edge(layout: Layout<'_>, cursor: Point) -> Option<Edge> {
|
fn in_edge(layout: Layout<'_>, cursor: Point) -> Option<Edge> {
|
||||||
let bounds = layout.bounds();
|
let bounds = layout.bounds();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -403,15 +403,6 @@ pub enum Action {
|
||||||
///
|
///
|
||||||
/// [`PaneGrid`]: super::PaneGrid
|
/// [`PaneGrid`]: super::PaneGrid
|
||||||
Idle,
|
Idle,
|
||||||
/// A [`Pane`] in the [`PaneGrid`] is being clicked.
|
|
||||||
///
|
|
||||||
/// [`PaneGrid`]: super::PaneGrid
|
|
||||||
Clicking {
|
|
||||||
/// The [`Pane`] being clicked.
|
|
||||||
pane: Pane,
|
|
||||||
/// The starting [`Point`] of the click interaction.
|
|
||||||
origin: Point,
|
|
||||||
},
|
|
||||||
/// A [`Pane`] in the [`PaneGrid`] is being dragged.
|
/// A [`Pane`] in the [`PaneGrid`] is being dragged.
|
||||||
///
|
///
|
||||||
/// [`PaneGrid`]: super::PaneGrid
|
/// [`PaneGrid`]: super::PaneGrid
|
||||||
|
|
@ -441,14 +432,6 @@ impl Action {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current [`Pane`] that is being clicked, if any.
|
|
||||||
pub fn clicked_pane(&self) -> Option<(Pane, Point)> {
|
|
||||||
match *self {
|
|
||||||
Action::Clicking { pane, origin, .. } => Some((pane, origin)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the current [`Split`] that is being dragged, if any.
|
/// Returns the current [`Split`] that is being dragged, if any.
|
||||||
pub fn picked_split(&self) -> Option<(Split, Axis)> {
|
pub fn picked_split(&self) -> Option<(Split, Axis)> {
|
||||||
match *self {
|
match *self {
|
||||||
|
|
|
||||||
|
|
@ -106,15 +106,10 @@ where
|
||||||
child: impl Into<Element<'a, Message, Theme, Renderer>>,
|
child: impl Into<Element<'a, Message, Theme, Renderer>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let child = child.into();
|
let child = child.into();
|
||||||
let size = child.as_widget().size_hint();
|
let child_size = child.as_widget().size_hint();
|
||||||
|
|
||||||
if size.width.is_fill() {
|
self.width = self.width.enclose(child_size.width);
|
||||||
self.width = Length::Fill;
|
self.height = self.height.enclose(child_size.height);
|
||||||
}
|
|
||||||
|
|
||||||
if size.height.is_fill() {
|
|
||||||
self.height = Length::Fill;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.children.push(child);
|
self.children.push(child);
|
||||||
self
|
self
|
||||||
|
|
|
||||||
|
|
@ -907,7 +907,15 @@ pub fn draw<Theme, Renderer>(
|
||||||
theme.active(style)
|
theme.active(style)
|
||||||
};
|
};
|
||||||
|
|
||||||
let idle_scrollbar = theme.active(style).scrollbar;
|
let scrollbar_style = |is_dragging: bool, mouse_over_scrollbar: bool| {
|
||||||
|
if is_dragging {
|
||||||
|
theme.dragging(style).scrollbar
|
||||||
|
} else if cursor_over_scrollable.is_some() {
|
||||||
|
theme.hovered(style, mouse_over_scrollbar).scrollbar
|
||||||
|
} else {
|
||||||
|
theme.active(style).scrollbar
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
container::draw_background(
|
container::draw_background(
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -984,13 +992,10 @@ pub fn draw<Theme, Renderer>(
|
||||||
if let Some(scrollbar) = scrollbars.y {
|
if let Some(scrollbar) = scrollbars.y {
|
||||||
draw_scrollbar(
|
draw_scrollbar(
|
||||||
renderer,
|
renderer,
|
||||||
if mouse_over_y_scrollbar
|
scrollbar_style(
|
||||||
|| state.y_scroller_grabbed_at.is_some()
|
state.y_scroller_grabbed_at.is_some(),
|
||||||
{
|
mouse_over_y_scrollbar,
|
||||||
appearance.scrollbar
|
),
|
||||||
} else {
|
|
||||||
idle_scrollbar
|
|
||||||
},
|
|
||||||
&scrollbar,
|
&scrollbar,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -998,13 +1003,10 @@ pub fn draw<Theme, Renderer>(
|
||||||
if let Some(scrollbar) = scrollbars.x {
|
if let Some(scrollbar) = scrollbars.x {
|
||||||
draw_scrollbar(
|
draw_scrollbar(
|
||||||
renderer,
|
renderer,
|
||||||
if mouse_over_x_scrollbar
|
scrollbar_style(
|
||||||
|| state.x_scroller_grabbed_at.is_some()
|
state.x_scroller_grabbed_at.is_some(),
|
||||||
{
|
mouse_over_x_scrollbar,
|
||||||
appearance.scrollbar
|
),
|
||||||
} else {
|
|
||||||
idle_scrollbar
|
|
||||||
},
|
|
||||||
&scrollbar,
|
&scrollbar,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -686,6 +686,37 @@ impl Update {
|
||||||
text,
|
text,
|
||||||
..
|
..
|
||||||
} if state.is_focused => {
|
} if state.is_focused => {
|
||||||
|
match key.as_ref() {
|
||||||
|
keyboard::Key::Named(key::Named::Enter) => {
|
||||||
|
return edit(Edit::Enter);
|
||||||
|
}
|
||||||
|
keyboard::Key::Named(key::Named::Backspace) => {
|
||||||
|
return edit(Edit::Backspace);
|
||||||
|
}
|
||||||
|
keyboard::Key::Named(key::Named::Delete) => {
|
||||||
|
return edit(Edit::Delete);
|
||||||
|
}
|
||||||
|
keyboard::Key::Named(key::Named::Escape) => {
|
||||||
|
return Some(Self::Unfocus);
|
||||||
|
}
|
||||||
|
keyboard::Key::Character("c")
|
||||||
|
if modifiers.command() =>
|
||||||
|
{
|
||||||
|
return Some(Self::Copy);
|
||||||
|
}
|
||||||
|
keyboard::Key::Character("x")
|
||||||
|
if modifiers.command() =>
|
||||||
|
{
|
||||||
|
return Some(Self::Cut);
|
||||||
|
}
|
||||||
|
keyboard::Key::Character("v")
|
||||||
|
if modifiers.command() && !modifiers.alt() =>
|
||||||
|
{
|
||||||
|
return Some(Self::Paste);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(text) = text {
|
if let Some(text) = text {
|
||||||
if let Some(c) = text.chars().find(|c| !c.is_control())
|
if let Some(c) = text.chars().find(|c| !c.is_control())
|
||||||
{
|
{
|
||||||
|
|
@ -711,36 +742,7 @@ impl Update {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match key.as_ref() {
|
None
|
||||||
keyboard::Key::Named(key::Named::Enter) => {
|
|
||||||
edit(Edit::Enter)
|
|
||||||
}
|
|
||||||
keyboard::Key::Named(key::Named::Backspace) => {
|
|
||||||
edit(Edit::Backspace)
|
|
||||||
}
|
|
||||||
keyboard::Key::Named(key::Named::Delete) => {
|
|
||||||
edit(Edit::Delete)
|
|
||||||
}
|
|
||||||
keyboard::Key::Named(key::Named::Escape) => {
|
|
||||||
Some(Self::Unfocus)
|
|
||||||
}
|
|
||||||
keyboard::Key::Character("c")
|
|
||||||
if modifiers.command() =>
|
|
||||||
{
|
|
||||||
Some(Self::Copy)
|
|
||||||
}
|
|
||||||
keyboard::Key::Character("x")
|
|
||||||
if modifiers.command() =>
|
|
||||||
{
|
|
||||||
Some(Self::Cut)
|
|
||||||
}
|
|
||||||
keyboard::Key::Character("v")
|
|
||||||
if modifiers.command() && !modifiers.alt() =>
|
|
||||||
{
|
|
||||||
Some(Self::Paste)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -762,12 +762,94 @@ where
|
||||||
let modifiers = state.keyboard_modifiers;
|
let modifiers = state.keyboard_modifiers;
|
||||||
focus.updated_at = Instant::now();
|
focus.updated_at = Instant::now();
|
||||||
|
|
||||||
|
match key.as_ref() {
|
||||||
|
keyboard::Key::Character("c")
|
||||||
|
if state.keyboard_modifiers.command() =>
|
||||||
|
{
|
||||||
|
if let Some((start, end)) =
|
||||||
|
state.cursor.selection(value)
|
||||||
|
{
|
||||||
|
clipboard.write(
|
||||||
|
clipboard::Kind::Standard,
|
||||||
|
value.select(start, end).to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return event::Status::Captured;
|
||||||
|
}
|
||||||
|
keyboard::Key::Character("x")
|
||||||
|
if state.keyboard_modifiers.command() =>
|
||||||
|
{
|
||||||
|
if let Some((start, end)) =
|
||||||
|
state.cursor.selection(value)
|
||||||
|
{
|
||||||
|
clipboard.write(
|
||||||
|
clipboard::Kind::Standard,
|
||||||
|
value.select(start, end).to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut editor = Editor::new(value, &mut state.cursor);
|
||||||
|
editor.delete();
|
||||||
|
|
||||||
|
let message = (on_input)(editor.contents());
|
||||||
|
shell.publish(message);
|
||||||
|
|
||||||
|
update_cache(state, value);
|
||||||
|
|
||||||
|
return event::Status::Captured;
|
||||||
|
}
|
||||||
|
keyboard::Key::Character("v")
|
||||||
|
if state.keyboard_modifiers.command()
|
||||||
|
&& !state.keyboard_modifiers.alt() =>
|
||||||
|
{
|
||||||
|
let content = match state.is_pasting.take() {
|
||||||
|
Some(content) => content,
|
||||||
|
None => {
|
||||||
|
let content: String = clipboard
|
||||||
|
.read(clipboard::Kind::Standard)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.chars()
|
||||||
|
.filter(|c| !c.is_control())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Value::new(&content)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut editor = Editor::new(value, &mut state.cursor);
|
||||||
|
|
||||||
|
editor.paste(content.clone());
|
||||||
|
|
||||||
|
let message = if let Some(paste) = &on_paste {
|
||||||
|
(paste)(editor.contents())
|
||||||
|
} else {
|
||||||
|
(on_input)(editor.contents())
|
||||||
|
};
|
||||||
|
shell.publish(message);
|
||||||
|
|
||||||
|
state.is_pasting = Some(content);
|
||||||
|
|
||||||
|
update_cache(state, value);
|
||||||
|
|
||||||
|
return event::Status::Captured;
|
||||||
|
}
|
||||||
|
keyboard::Key::Character("a")
|
||||||
|
if state.keyboard_modifiers.command() =>
|
||||||
|
{
|
||||||
|
state.cursor.select_all(value);
|
||||||
|
|
||||||
|
return event::Status::Captured;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(text) = text {
|
if let Some(text) = text {
|
||||||
state.is_pasting = None;
|
state.is_pasting = None;
|
||||||
|
|
||||||
let c = text.chars().next().unwrap_or_default();
|
if let Some(c) =
|
||||||
|
text.chars().next().filter(|c| !c.is_control())
|
||||||
if !c.is_control() {
|
{
|
||||||
let mut editor = Editor::new(value, &mut state.cursor);
|
let mut editor = Editor::new(value, &mut state.cursor);
|
||||||
|
|
||||||
editor.insert(c);
|
editor.insert(c);
|
||||||
|
|
@ -880,76 +962,6 @@ where
|
||||||
state.cursor.move_to(value.len());
|
state.cursor.move_to(value.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
keyboard::Key::Character("c")
|
|
||||||
if state.keyboard_modifiers.command() =>
|
|
||||||
{
|
|
||||||
if let Some((start, end)) =
|
|
||||||
state.cursor.selection(value)
|
|
||||||
{
|
|
||||||
clipboard.write(
|
|
||||||
clipboard::Kind::Standard,
|
|
||||||
value.select(start, end).to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyboard::Key::Character("x")
|
|
||||||
if state.keyboard_modifiers.command() =>
|
|
||||||
{
|
|
||||||
if let Some((start, end)) =
|
|
||||||
state.cursor.selection(value)
|
|
||||||
{
|
|
||||||
clipboard.write(
|
|
||||||
clipboard::Kind::Standard,
|
|
||||||
value.select(start, end).to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut editor = Editor::new(value, &mut state.cursor);
|
|
||||||
editor.delete();
|
|
||||||
|
|
||||||
let message = (on_input)(editor.contents());
|
|
||||||
shell.publish(message);
|
|
||||||
|
|
||||||
update_cache(state, value);
|
|
||||||
}
|
|
||||||
keyboard::Key::Character("v")
|
|
||||||
if state.keyboard_modifiers.command()
|
|
||||||
&& !state.keyboard_modifiers.alt() =>
|
|
||||||
{
|
|
||||||
let content = match state.is_pasting.take() {
|
|
||||||
Some(content) => content,
|
|
||||||
None => {
|
|
||||||
let content: String = clipboard
|
|
||||||
.read(clipboard::Kind::Standard)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.chars()
|
|
||||||
.filter(|c| !c.is_control())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Value::new(&content)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut editor = Editor::new(value, &mut state.cursor);
|
|
||||||
|
|
||||||
editor.paste(content.clone());
|
|
||||||
|
|
||||||
let message = if let Some(paste) = &on_paste {
|
|
||||||
(paste)(editor.contents())
|
|
||||||
} else {
|
|
||||||
(on_input)(editor.contents())
|
|
||||||
};
|
|
||||||
shell.publish(message);
|
|
||||||
|
|
||||||
state.is_pasting = Some(content);
|
|
||||||
|
|
||||||
update_cache(state, value);
|
|
||||||
}
|
|
||||||
keyboard::Key::Character("a")
|
|
||||||
if state.keyboard_modifiers.command() =>
|
|
||||||
{
|
|
||||||
state.cursor.select_all(value);
|
|
||||||
}
|
|
||||||
keyboard::Key::Named(key::Named::Escape) => {
|
keyboard::Key::Named(key::Named::Escape) => {
|
||||||
state.is_focused = None;
|
state.is_focused = None;
|
||||||
state.is_dragging = false;
|
state.is_dragging = false;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::container;
|
||||||
use crate::core::event::{self, Event};
|
use crate::core::event::{self, Event};
|
||||||
use crate::core::layout;
|
use crate::core::layout;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
|
|
@ -6,9 +7,10 @@ use crate::core::renderer;
|
||||||
use crate::core::widget::tree::{self, Tree};
|
use crate::core::widget::tree::{self, Tree};
|
||||||
use crate::core::widget::Operation;
|
use crate::core::widget::Operation;
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Size, Vector,
|
Background, Clipboard, Element, Layout, Length, Point, Rectangle, Shell,
|
||||||
Widget,
|
Size, Vector, Widget,
|
||||||
};
|
};
|
||||||
|
use crate::style::application;
|
||||||
|
|
||||||
/// A widget that applies any `Theme` to its contents.
|
/// A widget that applies any `Theme` to its contents.
|
||||||
///
|
///
|
||||||
|
|
@ -18,14 +20,18 @@ use crate::core::{
|
||||||
pub struct Themer<'a, Message, Theme, Renderer>
|
pub struct Themer<'a, Message, Theme, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: crate::core::Renderer,
|
Renderer: crate::core::Renderer,
|
||||||
|
Theme: application::StyleSheet,
|
||||||
{
|
{
|
||||||
content: Element<'a, Message, Theme, Renderer>,
|
content: Element<'a, Message, Theme, Renderer>,
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
|
style: Theme::Style,
|
||||||
|
show_background: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message, Theme, Renderer> Themer<'a, Message, Theme, Renderer>
|
impl<'a, Message, Theme, Renderer> Themer<'a, Message, Theme, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: crate::core::Renderer,
|
Renderer: crate::core::Renderer,
|
||||||
|
Theme: application::StyleSheet,
|
||||||
{
|
{
|
||||||
/// Creates an empty [`Themer`] that applies the given `Theme`
|
/// Creates an empty [`Themer`] that applies the given `Theme`
|
||||||
/// to the provided `content`.
|
/// to the provided `content`.
|
||||||
|
|
@ -34,16 +40,25 @@ where
|
||||||
T: Into<Element<'a, Message, Theme, Renderer>>,
|
T: Into<Element<'a, Message, Theme, Renderer>>,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
theme,
|
|
||||||
content: content.into(),
|
content: content.into(),
|
||||||
|
theme,
|
||||||
|
style: Theme::Style::default(),
|
||||||
|
show_background: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets whether to draw the background color of the `Theme`.
|
||||||
|
pub fn background(mut self, background: bool) -> Self {
|
||||||
|
self.show_background = background;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, AnyTheme, Message, Theme, Renderer> Widget<Message, AnyTheme, Renderer>
|
impl<'a, AnyTheme, Message, Theme, Renderer> Widget<Message, AnyTheme, Renderer>
|
||||||
for Themer<'a, Message, Theme, Renderer>
|
for Themer<'a, Message, Theme, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: crate::core::Renderer,
|
Renderer: crate::core::Renderer,
|
||||||
|
Theme: application::StyleSheet,
|
||||||
{
|
{
|
||||||
fn tag(&self) -> tree::Tag {
|
fn tag(&self) -> tree::Tag {
|
||||||
self.content.as_widget().tag()
|
self.content.as_widget().tag()
|
||||||
|
|
@ -120,16 +135,33 @@ where
|
||||||
tree: &Tree,
|
tree: &Tree,
|
||||||
renderer: &mut Renderer,
|
renderer: &mut Renderer,
|
||||||
_theme: &AnyTheme,
|
_theme: &AnyTheme,
|
||||||
renderer_style: &renderer::Style,
|
_style: &renderer::Style,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
|
let appearance = self.theme.appearance(&self.style);
|
||||||
|
|
||||||
|
if self.show_background {
|
||||||
|
container::draw_background(
|
||||||
|
renderer,
|
||||||
|
&container::Appearance {
|
||||||
|
background: Some(Background::Color(
|
||||||
|
appearance.background_color,
|
||||||
|
)),
|
||||||
|
..container::Appearance::default()
|
||||||
|
},
|
||||||
|
layout.bounds(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
self.content.as_widget().draw(
|
self.content.as_widget().draw(
|
||||||
tree,
|
tree,
|
||||||
renderer,
|
renderer,
|
||||||
&self.theme,
|
&self.theme,
|
||||||
renderer_style,
|
&renderer::Style {
|
||||||
|
text_color: appearance.text_color,
|
||||||
|
},
|
||||||
layout,
|
layout,
|
||||||
cursor,
|
cursor,
|
||||||
viewport,
|
viewport,
|
||||||
|
|
@ -248,7 +280,7 @@ impl<'a, AnyTheme, Message, Theme, Renderer>
|
||||||
for Element<'a, Message, AnyTheme, Renderer>
|
for Element<'a, Message, AnyTheme, Renderer>
|
||||||
where
|
where
|
||||||
Message: 'a,
|
Message: 'a,
|
||||||
Theme: 'a,
|
Theme: 'a + application::StyleSheet,
|
||||||
Renderer: 'a + crate::core::Renderer,
|
Renderer: 'a + crate::core::Renderer,
|
||||||
{
|
{
|
||||||
fn from(
|
fn from(
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,10 @@ where
|
||||||
self.content.as_widget().size()
|
self.content.as_widget().size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> Size<Length> {
|
||||||
|
self.content.as_widget().size_hint()
|
||||||
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
tree: &mut widget::Tree,
|
tree: &mut widget::Tree,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue