merge: pop-os/iced a l'etat pinne par libcosmic 08a09e3df (epoch-1.9.0)
Some checks failed
Audit / vulnerabilities (push) Has been cancelled
Check / wasm (push) Has been cancelled
Check / widget (push) Has been cancelled
Document / all (push) Has been cancelled
Format / all (push) Has been cancelled
Lint / all (push) Has been cancelled
Test / all (macOS-latest, 1.88) (push) Has been cancelled
Test / all (macOS-latest, beta) (push) Has been cancelled
Test / all (macOS-latest, stable) (push) Has been cancelled
Test / all (ubuntu-latest, 1.88) (push) Has been cancelled
Test / all (ubuntu-latest, beta) (push) Has been cancelled
Test / all (ubuntu-latest, stable) (push) Has been cancelled
Test / all (windows-latest, 1.88) (push) Has been cancelled
Test / all (windows-latest, beta) (push) Has been cancelled
Test / all (windows-latest, stable) (push) Has been cancelled
Some checks failed
Audit / vulnerabilities (push) Has been cancelled
Check / wasm (push) Has been cancelled
Check / widget (push) Has been cancelled
Document / all (push) Has been cancelled
Format / all (push) Has been cancelled
Lint / all (push) Has been cancelled
Test / all (macOS-latest, 1.88) (push) Has been cancelled
Test / all (macOS-latest, beta) (push) Has been cancelled
Test / all (macOS-latest, stable) (push) Has been cancelled
Test / all (ubuntu-latest, 1.88) (push) Has been cancelled
Test / all (ubuntu-latest, beta) (push) Has been cancelled
Test / all (ubuntu-latest, stable) (push) Has been cancelled
Test / all (windows-latest, 1.88) (push) Has been cancelled
Test / all (windows-latest, beta) (push) Has been cancelled
Test / all (windows-latest, stable) (push) Has been cancelled
# Conflicts: # Cargo.toml
This commit is contained in:
commit
24da1dc0e4
23 changed files with 1602 additions and 1231 deletions
|
|
@ -9,7 +9,7 @@ pub use scrollable::Scrollable;
|
|||
pub use text_input::TextInput;
|
||||
|
||||
use crate::widget::Id;
|
||||
use crate::{Rectangle, Vector};
|
||||
use crate::{Rectangle, Vector, window};
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt;
|
||||
|
|
@ -75,6 +75,12 @@ pub trait Operation<T = ()>: Send {
|
|||
fn finish(&self) -> Outcome<T> {
|
||||
Outcome::None
|
||||
}
|
||||
|
||||
/// Track the active window id being processed if relevant to the operation
|
||||
fn set_window_id(&mut self, _id: window::Id) {}
|
||||
|
||||
/// Used to mark the beginning of operations for a widget and its children
|
||||
fn pre_operation(&mut self, _id: Option<&Id>) {}
|
||||
}
|
||||
|
||||
impl<T, O> Operation<O> for Box<T>
|
||||
|
|
@ -140,6 +146,14 @@ where
|
|||
fn finish(&self) -> Outcome<O> {
|
||||
self.as_ref().finish()
|
||||
}
|
||||
|
||||
fn set_window_id(&mut self, id: window::Id) {
|
||||
self.as_mut().set_window_id(id);
|
||||
}
|
||||
|
||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
||||
self.as_mut().pre_operation(id);
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of an [`Operation`].
|
||||
|
|
@ -243,6 +257,14 @@ where
|
|||
fn finish(&self) -> Outcome<O> {
|
||||
Outcome::None
|
||||
}
|
||||
|
||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
||||
self.operation.pre_operation(id);
|
||||
}
|
||||
|
||||
fn set_window_id(&mut self, id: window::Id) {
|
||||
self.operation.set_window_id(id);
|
||||
}
|
||||
}
|
||||
|
||||
BlackBox { operation }
|
||||
|
|
@ -341,6 +363,10 @@ where
|
|||
) {
|
||||
self.operation.custom(id, bounds, state);
|
||||
}
|
||||
|
||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
||||
self.operation.pre_operation(id);
|
||||
}
|
||||
}
|
||||
|
||||
self.operation.traverse(&mut |operation| {
|
||||
|
|
@ -410,6 +436,14 @@ where
|
|||
})),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_window_id(&mut self, id: window::Id) {
|
||||
self.operation.set_window_id(id);
|
||||
}
|
||||
|
||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
||||
self.operation.pre_operation(id);
|
||||
}
|
||||
}
|
||||
|
||||
Map {
|
||||
|
|
@ -515,6 +549,14 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_window_id(&mut self, id: window::Id) {
|
||||
self.operation.set_window_id(id);
|
||||
}
|
||||
|
||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
||||
self.operation.pre_operation(id);
|
||||
}
|
||||
}
|
||||
|
||||
Chain {
|
||||
|
|
@ -566,6 +608,14 @@ pub fn scoped<T: 'static>(
|
|||
outcome => outcome,
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
||||
self.operation.pre_operation(id);
|
||||
}
|
||||
|
||||
fn set_window_id(&mut self, id: window::Id) {
|
||||
self.operation.set_window_id(id);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedOperation {
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ pub struct State<P: Paragraph> {
|
|||
focused: bool,
|
||||
keyboard_focused: bool,
|
||||
context_menu_position: Option<Point>,
|
||||
clipboard_has_text: bool,
|
||||
}
|
||||
|
||||
impl<P: Paragraph> Default for State<P> {
|
||||
|
|
@ -234,6 +235,7 @@ impl<P: Paragraph> Default for State<P> {
|
|||
focused: false,
|
||||
keyboard_focused: false,
|
||||
context_menu_position: None,
|
||||
clipboard_has_text: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -377,45 +379,65 @@ where
|
|||
let style = theme.style(&self.class);
|
||||
let bounds = layout.bounds();
|
||||
let paragraph = state.paragraph.raw();
|
||||
if let Some(sel) = &state.selection {
|
||||
let left = sel.anchor.min(sel.end);
|
||||
let right = sel.anchor.max(sel.end);
|
||||
let content: &str = self.fragment.as_ref();
|
||||
let anchor = bounds.anchor(
|
||||
paragraph.min_bounds(),
|
||||
paragraph.align_x(),
|
||||
paragraph.align_y(),
|
||||
);
|
||||
|
||||
if left != right {
|
||||
let lo_byte = grapheme_to_byte(content, left);
|
||||
let hi_byte = grapheme_to_byte(content, right);
|
||||
let rects: Vec<Rectangle> = state
|
||||
.selection
|
||||
.as_ref()
|
||||
.filter(|sel| sel.anchor != sel.end)
|
||||
.map(|sel| {
|
||||
let content: &str = self.fragment.as_ref();
|
||||
let lo_byte =
|
||||
grapheme_to_byte(content, sel.anchor.min(sel.end));
|
||||
let hi_byte =
|
||||
grapheme_to_byte(content, sel.anchor.max(sel.end));
|
||||
|
||||
let anchor = bounds.anchor(
|
||||
paragraph.min_bounds(),
|
||||
paragraph.align_x(),
|
||||
paragraph.align_y(),
|
||||
paragraph
|
||||
.highlight(
|
||||
0,
|
||||
(lo_byte, text::Affinity::After),
|
||||
(hi_byte, text::Affinity::Before),
|
||||
)
|
||||
.into_iter()
|
||||
.map(|r| Rectangle {
|
||||
x: anchor.x + r.x,
|
||||
y: anchor.y + r.y,
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let fill_selection = |renderer: &mut Renderer| {
|
||||
for r in &rects {
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds: *r,
|
||||
..renderer::Quad::default()
|
||||
},
|
||||
style.selected_fill,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let rects = paragraph.highlight(
|
||||
0,
|
||||
(lo_byte, text::Affinity::After),
|
||||
(hi_byte, text::Affinity::Before),
|
||||
);
|
||||
|
||||
for r in rects {
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds: Rectangle {
|
||||
x: anchor.x + r.x,
|
||||
y: anchor.y + r.y,
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
},
|
||||
..renderer::Quad::default()
|
||||
},
|
||||
style.selected_fill,
|
||||
);
|
||||
}
|
||||
if style.selected_text_color.is_none() {
|
||||
fill_selection(renderer);
|
||||
}
|
||||
draw(renderer, defaults, bounds, paragraph, style, viewport);
|
||||
if let Some(color) = style.selected_text_color {
|
||||
fill_selection(renderer);
|
||||
for r in &rects {
|
||||
renderer.with_layer(*r, |renderer| {
|
||||
renderer
|
||||
.fill_paragraph(paragraph, anchor, color, *viewport);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
draw(renderer, defaults, bounds, paragraph, style, viewport);
|
||||
}
|
||||
|
||||
fn update(
|
||||
|
|
@ -529,6 +551,7 @@ where
|
|||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) => {
|
||||
if let Some(pos) = cursor.position_over(bounds) {
|
||||
state.context_menu_position = Some(pos);
|
||||
state.clipboard_has_text = clipboard_has_text(clipboard);
|
||||
state.focused = true;
|
||||
state.keyboard_focused = false;
|
||||
shell.capture_event();
|
||||
|
|
@ -928,6 +951,10 @@ pub struct Style {
|
|||
pub color: Option<Color>,
|
||||
/// The fill [`Color`] of the selection highlight.
|
||||
pub selected_fill: Color,
|
||||
/// The [`Color`] of selected text.
|
||||
///
|
||||
/// The default, `None`, keeps the regular text color.
|
||||
pub selected_text_color: Option<Color>,
|
||||
}
|
||||
|
||||
impl Default for Style {
|
||||
|
|
@ -935,6 +962,7 @@ impl Default for Style {
|
|||
Self {
|
||||
color: None,
|
||||
selected_fill: DEFAULT_SELECTION_COLOR,
|
||||
selected_text_color: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1023,6 +1051,13 @@ pub fn danger(theme: &Theme) -> Style {
|
|||
|
||||
use crate::widget::tree::Tree as WidgetTree;
|
||||
|
||||
/// Returns `true` if the clipboard currently holds non-empty text.
|
||||
pub fn clipboard_has_text(clipboard: &dyn Clipboard) -> bool {
|
||||
clipboard
|
||||
.read(crate::clipboard::Kind::Standard)
|
||||
.is_some_and(|s| !s.is_empty())
|
||||
}
|
||||
|
||||
/// Implement this on a **widget** to enable context menu support for
|
||||
/// text selection (Copy, Select All, and optionally Cut / Paste) in libcosmic
|
||||
pub trait HasSelectableText {
|
||||
|
|
@ -1032,11 +1067,23 @@ pub trait HasSelectableText {
|
|||
/// Selects all text.
|
||||
fn select_all(&self, tree: &mut WidgetTree);
|
||||
|
||||
/// Returns `true` if the widget is editable (enables Cut / Paste).
|
||||
/// Returns `true` if the widget is editable (shows Cut / Paste).
|
||||
fn is_editable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns `true` if the widget has any text (enables Select All).
|
||||
fn has_text(&self, _tree: &WidgetTree) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns whether the clipboard held text when the context menu was
|
||||
/// requested (enables Paste). Widgets cache this on right-click because
|
||||
/// overlays have no clipboard access.
|
||||
fn clipboard_has_text(&self, _tree: &WidgetTree) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns `true` if the widget is currently focused.
|
||||
fn is_focused(&self, tree: &WidgetTree) -> bool;
|
||||
|
||||
|
|
@ -1106,6 +1153,16 @@ impl<Theme: Catalog, Renderer: text::Renderer> HasSelectableText
|
|||
sel.end = count;
|
||||
}
|
||||
|
||||
fn has_text(&self, tree: &WidgetTree) -> bool {
|
||||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
||||
!state.paragraph.content().is_empty()
|
||||
}
|
||||
|
||||
fn clipboard_has_text(&self, tree: &WidgetTree) -> bool {
|
||||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
||||
state.clipboard_has_text
|
||||
}
|
||||
|
||||
fn is_focused(&self, tree: &WidgetTree) -> bool {
|
||||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
||||
state.is_focused()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue