Compare commits

..

No commits in common. "24da1dc0e4e5c1b7f803bf8584d10939fe5acdec" and "d12bb8e056712ab87efbdd4296261331772ec413" have entirely different histories.

23 changed files with 1228 additions and 1599 deletions

2119
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -10,10 +10,10 @@ async-io = ["accesskit_winit?/async-io"]
tokio = ["accesskit_winit?/tokio"]
[dependencies]
accesskit = { git = "https://github.com/wash2/accesskit", rev = "6c20249" }
accesskit_windows = { git = "https://github.com/wash2/accesskit", rev = "6c20249", optional = true }
accesskit_macos = { git = "https://github.com/wash2/accesskit", rev = "6c20249", optional = true }
accesskit_winit = { git = "https://github.com/wash2/accesskit", rev = "6c20249", optional = true, default-features = false, features = [
accesskit = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14" }
accesskit_windows = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14", optional = true }
accesskit_macos = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14", optional = true }
accesskit_winit = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14", optional = true, default-features = false, features = [
"rwh_06",
] }
# accesskit = { path = "../../../accesskit/common" }

View file

@ -9,7 +9,7 @@ pub use scrollable::Scrollable;
pub use text_input::TextInput;
use crate::widget::Id;
use crate::{Rectangle, Vector, window};
use crate::{Rectangle, Vector};
use std::any::Any;
use std::fmt;
@ -75,12 +75,6 @@ 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>
@ -146,14 +140,6 @@ 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`].
@ -257,14 +243,6 @@ 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 }
@ -363,10 +341,6 @@ 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| {
@ -436,14 +410,6 @@ 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 {
@ -549,14 +515,6 @@ 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 {
@ -608,14 +566,6 @@ 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 {

View file

@ -224,7 +224,6 @@ 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> {
@ -235,7 +234,6 @@ impl<P: Paragraph> Default for State<P> {
focused: false,
keyboard_focused: false,
context_menu_position: None,
clipboard_has_text: false,
}
}
}
@ -379,65 +377,45 @@ where
let style = theme.style(&self.class);
let bounds = layout.bounds();
let paragraph = state.paragraph.raw();
let anchor = bounds.anchor(
paragraph.min_bounds(),
paragraph.align_x(),
paragraph.align_y(),
);
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 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));
if left != right {
let lo_byte = grapheme_to_byte(content, left);
let hi_byte = grapheme_to_byte(content, right);
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 anchor = bounds.anchor(
paragraph.min_bounds(),
paragraph.align_x(),
paragraph.align_y(),
);
}
};
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);
});
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,
);
}
}
}
draw(renderer, defaults, bounds, paragraph, style, viewport);
}
fn update(
@ -551,7 +529,6 @@ 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();
@ -951,10 +928,6 @@ 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 {
@ -962,7 +935,6 @@ impl Default for Style {
Self {
color: None,
selected_fill: DEFAULT_SELECTION_COLOR,
selected_text_color: None,
}
}
}
@ -1051,13 +1023,6 @@ 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 {
@ -1067,23 +1032,11 @@ pub trait HasSelectableText {
/// Selects all text.
fn select_all(&self, tree: &mut WidgetTree);
/// Returns `true` if the widget is editable (shows Cut / Paste).
/// Returns `true` if the widget is editable (enables 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;
@ -1153,16 +1106,6 @@ 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()

View file

@ -157,11 +157,6 @@ pub enum Action {
/// padding of the surface geometry
padding: IcedMargin,
},
ShowOnLock {
/// id of the layer surface
id: Id,
value: bool,
}
}
impl fmt::Debug for Action {
@ -220,11 +215,6 @@ impl fmt::Debug for Action {
"Action::LayerSurfaceAction::Padding {{ id: {:#?}, padding: {:?} }}",
id, padding
),
Action::ShowOnLock { id, value } => write!(
f,
"Action::LayerSurfaceAction::ShowOnLock {{ id: {:#?}, value: {:?} }}",
id, value
),
}
}
}

View file

@ -1,6 +1,6 @@
//! Find and query widgets in your applications.
pub use iced_selector::{
Bounded, Candidate, Selector, Target, Text, focus, id, is_focused,
Bounded, Candidate, Selector, Target, Text, id, is_focused,
};
use crate::Task;

View file

@ -1,5 +1,3 @@
use iced_core::window;
use crate::Selector;
use crate::core::widget::operation::{
Focusable, Outcome, Scrollable, TextInput,
@ -102,10 +100,6 @@ where
fn finish(&self) -> Self::Output {
self.outputs.clone()
}
fn set_window_id(&mut self, id: window::Id) {
self.selector.set_window_id(id);
}
}
pub trait Strategy {
@ -116,8 +110,6 @@ pub trait Strategy {
fn is_done(&self) -> bool;
fn finish(&self) -> Self::Output;
fn set_window_id(&mut self, _id: window::Id) {}
}
#[derive(Debug)]
@ -281,12 +273,4 @@ where
fn finish(&self) -> Outcome<S::Output> {
Outcome::Some(self.strategy.finish())
}
fn set_window_id(&mut self, id: window::Id) {
self.strategy.set_window_id(id);
}
fn pre_operation(&mut self, id: Option<&Id>) {
self.strategy.feed(Candidate::PreOperation { id });
}
}

View file

@ -1,6 +1,5 @@
//! Select data from the widget tree.
use iced_core as core;
use iced_core::window;
mod find;
mod target;
@ -49,8 +48,6 @@ pub trait Selector {
{
FindAll::new(find::All::new(self))
}
fn set_window_id(&mut self, _id: window::Id) {}
}
impl Selector for &str {
@ -174,42 +171,3 @@ pub fn is_focused() -> impl Selector<Output = Target> {
IsFocused
}
/// Returns a [`Selector`] that matches widgets that are focusable.
pub fn focus() -> impl Selector<Output = (bool, Target, window::Id)> {
struct Focus {
cur_window_id: window::Id,
}
impl Selector for Focus {
type Output = (bool, Target, window::Id);
fn select(&mut self, candidate: Candidate<'_>) -> Option<Self::Output> {
if let Candidate::Focusable { state, .. } = candidate {
Some((
state.is_focused(),
Target::from(candidate),
self.cur_window_id,
))
} else if matches!(candidate, Candidate::Scrollable { .. }) {
Some((false, Target::from(candidate), self.cur_window_id))
} else if matches!(candidate, Candidate::PreOperation { .. }) {
Some((false, Target::from(candidate), self.cur_window_id))
} else {
None
}
}
fn description(&self) -> String {
"is focusable and focused".to_owned()
}
fn set_window_id(&mut self, id: iced_core::window::Id) {
self.cur_window_id = id;
}
}
Focus {
cur_window_id: window::Id::NONE,
}
}

View file

@ -42,9 +42,6 @@ pub enum Target {
bounds: Rectangle,
visible_bounds: Option<Rectangle>,
},
PreOperation {
id: Option<Id>,
},
}
impl Target {
@ -57,7 +54,6 @@ impl Target {
| Target::TextInput { bounds, .. }
| Target::Text { bounds, .. }
| Target::Custom { bounds, .. } => *bounds,
Target::PreOperation { .. } => Rectangle::default(),
}
}
@ -70,7 +66,6 @@ impl Target {
| Target::TextInput { visible_bounds, .. }
| Target::Text { visible_bounds, .. }
| Target::Custom { visible_bounds, .. } => *visible_bounds,
Target::PreOperation { .. } => None,
}
}
}
@ -143,9 +138,6 @@ impl From<Candidate<'_>> for Target {
bounds,
visible_bounds,
},
Candidate::PreOperation { id } => {
Self::PreOperation { id: id.cloned() }
}
}
}
}
@ -203,8 +195,6 @@ pub enum Candidate<'a> {
visible_bounds: Option<Rectangle>,
state: &'a dyn Any,
},
/// Used to mark the beginning of operations for a widget and its children
PreOperation { id: Option<&'a Id> },
}
impl<'a> Candidate<'a> {
@ -216,8 +206,7 @@ impl<'a> Candidate<'a> {
| Candidate::Scrollable { id, .. }
| Candidate::TextInput { id, .. }
| Candidate::Text { id, .. }
| Candidate::Custom { id, .. }
| Candidate::PreOperation { id } => *id,
| Candidate::Custom { id, .. } => *id,
}
}
@ -230,7 +219,6 @@ impl<'a> Candidate<'a> {
| Candidate::TextInput { bounds, .. }
| Candidate::Text { bounds, .. }
| Candidate::Custom { bounds, .. } => *bounds,
Candidate::PreOperation { .. } => Rectangle::default(),
}
}
@ -243,7 +231,6 @@ impl<'a> Candidate<'a> {
| Candidate::TextInput { visible_bounds, .. }
| Candidate::Text { visible_bounds, .. }
| Candidate::Custom { visible_bounds, .. } => *visible_bounds,
Candidate::PreOperation { .. } => None,
}
}
}

View file

@ -273,11 +273,6 @@ impl<P: Program + 'static> Emulator<P> {
runtime::Action::Reload => {
// TODO
}
runtime::Action::Dnd(_)
| runtime::Action::PlatformSpecific(_) => {
// Drag and drop and platform surfaces need a
// windowing system, which an emulator has none of.
}
},
}
}
@ -479,9 +474,7 @@ impl<P: Program + 'static> Emulator<P> {
&mut self.renderer,
theme,
&renderer::Style {
icon_color: style.text_color,
text_color: style.text_color,
scale_factor: f64::from(scale_factor),
},
mouse::Cursor::Unavailable,
);

View file

@ -209,19 +209,17 @@ where
&mut self.messages,
);
let scale_factor = 2.0_f32;
self.raw.draw(
&mut self.renderer,
theme,
&core::renderer::Style {
icon_color: base.text_color,
text_color: base.text_color,
scale_factor: f64::from(scale_factor),
},
self.cursor,
);
let scale_factor = 2.0;
let physical_size = Size::new(
(self.size.width * scale_factor).round() as u32,
(self.size.height * scale_factor).round() as u32,

View file

@ -267,7 +267,6 @@ struct State {
is_hovered: bool,
is_pressed: bool,
is_focused: bool,
needs_redraw: bool,
}
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
@ -369,12 +368,6 @@ where
viewport,
);
let state = tree.state.downcast_mut::<State>();
if state.needs_redraw {
state.needs_redraw = false;
shell.request_redraw();
}
if shell.is_event_captured() {
return;
}
@ -784,11 +777,11 @@ impl Default for Style {
///
/// impl Catalog for MyTheme {
/// type Class<'a> = ButtonClass;
///
///
/// fn default<'a>() -> Self::Class<'a> {
/// ButtonClass::default()
/// }
///
///
///
/// fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
/// let mut style = Style::default();
@ -1007,12 +1000,10 @@ impl operation::Focusable for State {
}
fn focus(&mut self) {
self.needs_redraw |= !self.is_focused;
self.is_focused = true;
}
fn unfocus(&mut self) {
self.needs_redraw |= self.is_focused;
self.is_focused = false;
}
}

View file

@ -247,19 +247,64 @@ where
self
}
/// Adds padding at the ends of the [`Scrollbar`]s of the [`Scrollable`].
/// Sets whether the user should be allowed to auto-scroll the [`Scrollable`]
/// with the middle mouse button.
///
/// The `padding` provided will be used as space at the top and bottom of a
/// vertical [`Scrollbar`], and at the left and right ends of a horizontal
/// [`Scrollbar`], when they are visible.
///
/// Unlike [`Self::spacing`], the padding does not affect the layout of the
/// [`Scrollable`].
pub fn padding(mut self, new_padding: impl Into<Pixels>) -> Self {
let padding = new_padding.into().0;
/// By default, it is disabled.
pub fn auto_scroll(mut self, auto_scroll: bool) -> Self {
self.auto_scroll = auto_scroll;
self
}
/// Sets the scrollbar width of the [`Scrollbar`].
pub fn scrollbar_width(mut self, width: impl Into<Pixels>) -> Self {
let width = width.into().0.max(0.0);
match &mut self.direction {
Direction::Horizontal(scrollbar) | Direction::Vertical(scrollbar) => {
Direction::Horizontal(scrollbar)
| Direction::Vertical(scrollbar) => {
scrollbar.width = width;
}
Direction::Both {
horizontal,
vertical,
} => {
horizontal.width = width;
vertical.width = width;
}
}
self
}
/// Sets the scroller width of the [`Scrollbar`].
pub fn scroller_width(mut self, width: impl Into<Pixels>) -> Self {
let width = width.into().0.max(0.0);
match &mut self.direction {
Direction::Horizontal(scrollbar)
| Direction::Vertical(scrollbar) => {
scrollbar.scroller_width = width;
}
Direction::Both {
horizontal,
vertical,
} => {
horizontal.scroller_width = width;
vertical.scroller_width = width;
}
}
self
}
/// Sets the padding at the start and end of the [`Scrollbar`].
pub fn scrollbar_padding(mut self, padding: impl Into<Pixels>) -> Self {
let padding = padding.into().0.max(0.0);
match &mut self.direction {
Direction::Horizontal(scrollbar)
| Direction::Vertical(scrollbar) => {
scrollbar.padding = padding;
}
Direction::Both {
@ -274,15 +319,6 @@ where
self
}
/// Sets whether the user should be allowed to auto-scroll the [`Scrollable`]
/// with the middle mouse button.
///
/// By default, it is disabled.
pub fn auto_scroll(mut self, auto_scroll: bool) -> Self {
self.auto_scroll = auto_scroll;
self
}
/// Sets the style of this [`Scrollable`].
#[must_use]
pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self
@ -467,15 +503,9 @@ impl Scrollbar {
self
}
/// Sets the padding of the [`Scrollbar`].
///
/// The padding is added at the top and bottom of the scrollbar (or at the
/// left and right ends for a horizontal [`Scrollbar`]) when it is visible.
///
/// Unlike [`Self::margin`] and [`Self::spacing`], the padding does not
/// affect the layout of the [`Scrollable`].
/// Sets the padding at the start and end of the [`Scrollbar`].
pub fn padding(mut self, padding: impl Into<Pixels>) -> Self {
self.padding = padding.into().0;
self.padding = padding.into().0.max(0.0);
self
}
}
@ -645,7 +675,13 @@ where
let translation =
state.translation(self.direction, bounds, content_bounds);
operation.pre_operation(Some(&self.id));
operation.scrollable(
Some(&self.id),
bounds,
content_bounds,
translation,
state,
);
operation.traverse(&mut |operation| {
self.content.as_widget_mut().operate(
@ -659,14 +695,6 @@ where
operation,
);
});
// XXX must be done after traversing to perform DFS
operation.scrollable(
Some(&self.id),
bounds,
content_bounds,
translation,
state,
);
}
fn update(
@ -2383,16 +2411,13 @@ impl Scrollbars {
let total_scrollbar_width =
width.max(scroller_width) + 2.0 * margin;
// The padding is purely visual: it shrinks the top and bottom of the
// scrollbar without affecting the layout
let scrollbar_height = (bounds.height - x_scrollbar_height - 2.0 * padding).max(0.0);
// Total bounds of the scrollbar + margin + scroller width
let total_scrollbar_bounds = Rectangle {
x: bounds.x + bounds.width - total_scrollbar_width,
y: bounds.y + padding,
width: total_scrollbar_width,
height: scrollbar_height,
height: (bounds.height - x_scrollbar_height - 2.0 * padding)
.max(0.0),
};
// Bounds of just the scrollbar
@ -2402,7 +2427,8 @@ impl Scrollbars {
- width / 2.0,
y: bounds.y + padding,
width,
height: scrollbar_height,
height: (bounds.height - x_scrollbar_height - 2.0 * padding)
.max(0.0),
};
let ratio = bounds.height / content_bounds.height;
@ -2459,15 +2485,12 @@ impl Scrollbars {
let total_scrollbar_height =
width.max(scroller_width) + 2.0 * margin;
// The padding is purely visual: it shrinks the left and right ends of
// the scrollbar without affecting the layout
let scrollbar_width = (bounds.width - scrollbar_y_width - 2.0 * padding).max(0.0);
// Total bounds of the scrollbar + margin + scroller width
let total_scrollbar_bounds = Rectangle {
x: bounds.x + padding,
y: bounds.y + bounds.height - total_scrollbar_height,
width: scrollbar_width,
width: (bounds.width - scrollbar_y_width - 2.0 * padding)
.max(0.0),
height: total_scrollbar_height,
};
@ -2477,7 +2500,8 @@ impl Scrollbars {
y: bounds.y + bounds.height
- total_scrollbar_height / 2.0
- width / 2.0,
width: scrollbar_width,
width: (bounds.width - scrollbar_y_width - 2.0 * padding)
.max(0.0),
height: width,
};

View file

@ -545,7 +545,6 @@ pub struct State<Highlighter: text::Highlighter> {
highlighter_settings: Highlighter::Settings,
highlighter_format_address: usize,
context_menu_position: Option<Point>,
clipboard_has_text: bool,
pending_edit: Option<Action>,
}
@ -633,7 +632,6 @@ where
highlighter_settings: self.highlighter_settings.clone(),
highlighter_format_address: self.highlighter_format as usize,
context_menu_position: None,
clipboard_has_text: false,
pending_edit: None,
})
}
@ -738,8 +736,6 @@ where
state.focus = Some(Focus::now());
}
state.context_menu_position = Some(pos);
state.clipboard_has_text =
widget::text::clipboard_has_text(clipboard);
shell.capture_event();
return;
}
@ -1653,16 +1649,6 @@ where
self.on_edit.is_some()
}
fn has_text(&self, _tree: &widget::Tree) -> bool {
!self.content.is_empty()
}
fn clipboard_has_text(&self, tree: &widget::Tree) -> bool {
tree.state
.downcast_ref::<State<Highlighter>>()
.clipboard_has_text
}
fn is_focused(&self, tree: &widget::Tree) -> bool {
tree.state.downcast_ref::<State<Highlighter>>().is_focused()
}

View file

@ -269,13 +269,6 @@ pub fn window_event(
scale_factor,
)))
}
winit::event::PointerSource::TabletTool { .. } => {
let position = position.to_logical::<f64>(scale_factor);
Some(Event::Mouse(mouse::Event::CursorMoved {
position: Point::new(position.x as f32, position.y as f32),
}))
}
_ => None,
},
// TODO how to handle this for Touch? Is it different than a press? Need to double check winit impl
@ -313,24 +306,6 @@ pub fn window_event(
}
}))
}
WindowEvent::PointerButton {
button: ButtonSource::TabletTool { button, .. },
state,
..
} => {
let button: Option<winit::event::MouseButton> = button.into();
button.map(mouse_button).map(|button| {
Event::Mouse(match state {
winit::event::ElementState::Pressed => {
mouse::Event::ButtonPressed(button)
}
winit::event::ElementState::Released => {
mouse::Event::ButtonReleased(button)
}
})
})
}
WindowEvent::PointerButton {
button: ButtonSource::Touch { finger_id, force },
state,

View file

@ -69,8 +69,6 @@ use crate::runtime::user_interface::{self, UserInterface};
use crate::runtime::{Action, Task};
use program::Program;
#[cfg(feature = "program")]
pub use program::Program;
use window::WindowManager;
use rustc_hash::FxHashMap;
@ -1330,17 +1328,6 @@ async fn run_instance<P>(
continue;
}
}
// to avoid race with the reported modifier state from the popup of
// this window, don't empty the modifiers, on focus loss
#[cfg(wayland_platform)]
if let winit::event::WindowEvent::ModifiersChanged(modifiers) =
&event
&& modifiers.state().is_empty()
&& platform_specific_handler.has_popup(id)
{
continue;
}
match event {
winit::event::WindowEvent::SurfaceResized(_) => {
window.raw.request_redraw();
@ -2534,8 +2521,6 @@ where
while let Some(mut operation) = current_operation.take() {
for (id, ui) in interfaces.iter_mut() {
if let Some(window) = window_manager.get_mut(*id) {
operation.as_mut().set_window_id(*id);
ui.operate(&window.renderer, operation.as_mut());
}
}
@ -2654,10 +2639,10 @@ where
.and_then(|window| theme::Base::palette(window.state.theme()))
});
window_manager
.iter_mut()
.filter_map(|(id, window)| {
let cache = cached_user_interfaces.remove(&id)?;
cached_user_interfaces
.drain()
.filter_map(|(id, cache)| {
let window = window_manager.get_mut(id)?;
Some((
id,
@ -2730,5 +2715,7 @@ fn system_information(
graphics_backend: graphics.backend,
}
}
#[cfg(feature = "program")]
pub use program::Program;
pub use platform_specific::*;

View file

@ -74,11 +74,6 @@ impl PlatformSpecific {
}
}
#[cfg(wayland_platform)]
pub(crate) fn has_popup(&self, toplevel: window::Id) -> bool {
self.wayland.has_popup(toplevel)
}
pub(crate) fn retain_subsurfaces<F: Fn(window::Id) -> bool>(
&mut self,
keep: F,

View file

@ -138,14 +138,3 @@ pub fn set_input_zone<Message>(
)),
))
}
pub fn set_show_on_lock<Message>(
id: SurfaceId,
value: bool,
) -> Task<Message> {
task::effect(Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
wayland::layer_surface::Action::ShowOnLock { id, value },
)),
))
}

View file

@ -392,7 +392,6 @@ impl SctkEventLoop {
).ok(),
text_input_manager: TextInputManager::try_new(&registry_state, &qh),
ext_background_effect_manager: ext_background_effect::ExtBackgroundEffectManager::new(&globals, &qh).ok(),
cosmic_session_lock_layer_manager: registry_state.bind_one(&qh, 1..=1, ()).ok(),
registry_state,
queue_handle: qh,
@ -412,7 +411,6 @@ impl SctkEventLoop {
lock_surfaces: Vec::new(),
subsurfaces: Vec::new(),
touch_points: HashMap::new(),
kbd_leave_to_own_popup: HashSet::new(),
sctk_events: Vec::new(),
frame_status: HashMap::new(),
cursor_requests: HashMap::new(),

View file

@ -44,7 +44,6 @@ use cctk::{
cosmic_protocols::{
corner_radius::v1::client::cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1,
overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1,
session_lock_layer::v1::client::cosmic_session_lock_layer_manager_v1::CosmicSessionLockLayerManagerV1,
},
sctk::{
activation::{ActivationState, RequestData},
@ -435,9 +434,6 @@ pub struct SctkState {
pub(crate) blur_surfaces: HashMap<core::window::Id, ExtBackgroundEffectSurfaceV1>,
pub(crate) corner_radii: HashMap<core::window::Id, (SctkCornerRadius, Option<CornerRadius>)>,
pub(crate) touch_points: HashMap<touch::Finger, (WlSurface, Point)>,
/// List of layer surfaces whose keyboard leave was ignored because focus moved
/// to one of their own grabbing popups. Their next enter is swallowed too.
pub(crate) kbd_leave_to_own_popup: HashSet<ObjectId>,
/// Window updates, which are coming from SCTK or the compositor, which require
/// calling back to the sctk's downstream. They are handled right in the event loop,
@ -481,7 +477,6 @@ pub struct SctkState {
pub(crate) toplevel_manager: Option<ToplevelManagerState>,
pub(crate) subsurface_state: Option<SubsurfaceState>,
pub(crate) ext_background_effect_manager: Option<ext_background_effect::ExtBackgroundEffectManager>,
pub(crate) cosmic_session_lock_layer_manager: Option<CosmicSessionLockLayerManagerV1>,
pub(crate) activation_token_ctr: u32,
pub(crate) token_senders: HashMap<u32, oneshot::Sender<Option<String>>>,
@ -684,33 +679,6 @@ impl SctkState {
// TODO winit sets cursor size after handling the change for the window, so maybe that should be done as well.
}
/// Popups can only be attached to xdg or layer surfaces. So for subsurfaces, we return the
/// parent, and adjust the offset.
pub(crate) fn subsurface_popup_parent(
&self,
id: core::window::Id,
) -> Option<(WlSurface, (i32, i32))> {
let mut sub = self.subsurfaces.iter().find(|s| s.id == id)?;
let mut offset = (0, 0);
// bounded by the number of subsurfaces so a corrupt chain cannot spin forever
for _ in 0..=self.subsurfaces.len() {
if let Some(b) = sub.instance.bounds {
offset.0 += b.x as i32;
offset.1 += b.y as i32;
}
let parent = &sub.instance.parent;
match self
.subsurfaces
.iter()
.find(|s| s.instance.wl_surface == *parent)
{
Some(s) => sub = s,
None => return Some((parent.clone(), offset)),
}
}
None
}
pub fn get_popup(
&mut self,
settings: SctkPopupSettings,
@ -755,27 +723,6 @@ impl SctkState {
PopupParent::Popup(parent.popup.wl_surface().clone()),
parent.data.toplevel.clone(),
)
} else if let Some((surface, _)) =
self.subsurface_popup_parent(settings.parent)
{
if self
.layer_surfaces
.iter()
.any(|l| *l.surface.wl_surface() == surface)
{
(PopupParent::LayerSurface(surface.clone()), surface)
} else if self
.windows
.iter()
.any(|w| w.wl_surface(&self.connection) == surface)
{
(PopupParent::Window(surface.clone()), surface)
} else if let Some(p) = self.popmgr.popup(&surface) {
(PopupParent::Popup(surface), p.data.toplevel.clone())
} else {
// e.g. a session lock surface, which cannot parent xdg popups
return Err(PopupCreationError::ParentMissing);
}
} else {
return Err(PopupCreationError::ParentMissing);
};
@ -1209,7 +1156,6 @@ impl SctkState {
}
if let Some(i) = self.layer_surfaces.iter().position(|l| l.id == id) {
let l = self.layer_surfaces.remove(i);
_ = self.kbd_leave_to_own_popup.remove(&l.surface.wl_surface().id());
if let Some(blurred) = self.blur_surfaces.remove(&l.id) {
blurred.destroy();
@ -1320,36 +1266,16 @@ impl SctkState {
}
}
},
platform_specific::wayland::layer_surface::Action::ShowOnLock { id, value } => {
if let Some(layer_surface) = self.layer_surfaces.iter_mut().find(|l| l.id == id) {
if let SurfaceKind::Wlr(wlr) = layer_surface.surface.kind() {
if let Some(manager) = &self.cosmic_session_lock_layer_manager {
if value {
manager.set_show_on_lock(wlr);
} else {
manager.unset_show_on_lock(wlr);
}
_ = self.to_commit.insert(id, layer_surface.surface.wl_surface().clone());
}
}
}
}
},
Action::Popup(action) => {
match action {
platform_specific::wayland::popup::Action::Popup { popup: mut settings } => {
// anchor rects from widgets inside a subsurface are in that subsurface's coordinates
if let Some((_, (dx, dy))) = self.subsurface_popup_parent(settings.parent) {
settings.positioner.anchor_rect.x += dx;
settings.positioner.anchor_rect.y += dy;
}
platform_specific::wayland::popup::Action::Popup { popup: settings } => {
// first check existing popup
// if an existing popup with the same id exists, we just update the positioner.
if let Some((chain, existing)) = self.popmgr.popup_id(settings.id).and_then(|p|
(p.data.grab == settings.grab && self.popmgr.popups().any(|parent| parent.popup.wl_surface() == p.data.parent.wl_surface() && parent.data.id == settings.parent)
|| self.windows.iter().any(|w| w.id == settings.parent && *p.data.parent.wl_surface() == w.wl_surface(&self.connection))
|| self.layer_surfaces.iter().any(|l| l.id == settings.parent && p.data.parent.wl_surface() == l.surface.wl_surface())
|| (p.data.parent_window == settings.parent && self.subsurfaces.iter().any(|s| s.id == settings.parent))).then(||
|| self.layer_surfaces.iter().any(|l| l.id == settings.parent && p.data.parent.wl_surface() == l.surface.wl_surface())).then(||
{
p.popup.wl_surface().clone()
})).and_then(|s| self.popmgr.chain_for_popup_mut(&s))
@ -1720,13 +1646,9 @@ impl SctkState {
}
},
subsurface::Action::Reposition { id, x, y } => {
if let Some(subsurface) = self.subsurfaces.iter_mut().find(|s| s.id == id) {
if let Some(subsurface) = self.subsurfaces.iter().find(|s| s.id == id) {
subsurface.instance.wl_subsurface.set_position(x, y);
subsurface.instance.wl_surface.commit();
if let Some(b) = subsurface.instance.bounds.as_mut() {
b.x = x as f32;
b.y = y as f32;
}
}
},
},
@ -2243,4 +2165,3 @@ pub(crate) fn send_event(
delegate_noop!(SctkState: ignore WlSubsurface);
delegate_noop!(SctkState: ignore WlRegion);
delegate_noop!(SctkState: CosmicSessionLockLayerManagerV1);

View file

@ -1,16 +1,12 @@
use crate::platform_specific::wayland::{
event_loop::state::{PopupParent, SctkState},
event_loop::state::SctkState,
sctk_event::{KeyboardEventVariant, SctkEvent},
};
use cctk::sctk::{
delegate_keyboard,
seat::keyboard::{KeyboardHandler, Keysym, Modifiers},
};
use cctk::sctk::{
reexports::client::Proxy,
seat::keyboard::RawModifiers,
shell::{WaylandSurface, wlr_layer::KeyboardInteractivity},
};
use cctk::sctk::{reexports::client::Proxy, seat::keyboard::RawModifiers};
fn modifiers_from_keysyms(
keysyms: &[Keysym],
@ -94,11 +90,6 @@ impl KeyboardHandler for SctkState {
}
self.request_redraw(&surface);
// Focus is coming back from our own grabbing popup
if self.kbd_leave_to_own_popup.remove(&surface.id()) {
return;
}
let surfaces = self.subsurfaces.iter().filter_map(|s| {
(s.instance.parent == *surface).then(|| &s.instance.wl_surface)
});
@ -156,32 +147,6 @@ impl KeyboardHandler for SctkState {
_ = my_seat.kbd_focus.take();
(is_active, seat, kbd)
};
// A grabbing popup parented to this layer surface, or opened from one of
// its subsurfaces that holds focus, takes keyboard focus.
// Only swallow the leave when the layer surface has exclusive keyboard interactivity.
let focus_moved_to_own_popup = self.popmgr.popups().any(|p| {
p.data.grab
&& match &p.data.parent {
PopupParent::LayerSurface(s) => {
(s == surface
|| self.subsurfaces.iter().any(|sub| {
sub.id == p.data.parent_window
&& sub.instance.wl_surface == *surface
}))
&& self.layer_surfaces.iter().any(|l| {
l.surface.wl_surface() == s
&& l.keyboard_interactivity
== KeyboardInteractivity::Exclusive
})
}
_ => false,
}
});
if focus_moved_to_own_popup {
_ = self.kbd_leave_to_own_popup.insert(surface.id());
return;
}
let surfaces = self.subsurfaces.iter().filter_map(|s| {
(s.instance.parent == *surface).then(|| &s.instance.wl_surface)
});
@ -353,31 +318,6 @@ impl KeyboardHandler for SctkState {
surface: surface.clone(),
});
}
// A popup holds the seat's keyboard on behalf of its toplevel. Report
// the modifiers to the toplevel as well.
let toplevel = self.popmgr.popups().find_map(|p| {
(p.popup.wl_surface() == &surface)
.then(|| p.data.toplevel.clone())
});
if let Some(toplevel) = toplevel {
let id = self
.windows
.iter()
.find(|w| w.wl_surface(&self.connection) == toplevel)
.map(|w| w.id)
.or_else(|| {
self.layer_surfaces
.iter()
.find(|l| *l.surface.wl_surface() == toplevel)
.map(|l| l.id)
});
if let Some(toplevel) = id {
self.sctk_events.push(SctkEvent::PopupModifiers {
toplevel,
modifiers,
});
}
}
}
}
}

View file

@ -20,7 +20,7 @@ use iced_graphics::{Compositor, compositor};
use iced_runtime::core::{Vector, window};
use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
use raw_window_handle::{HasRawDisplayHandle, RawWindowHandle};
use sctk_event::{PopupEventVariant, SctkEvent};
use sctk_event::SctkEvent;
use std::sync::OnceLock;
use std::{collections::HashMap, sync::Arc};
use subsurface_widget::{SubsurfaceInstance, SubsurfaceState};
@ -103,7 +103,6 @@ pub(crate) struct WaylandSpecific {
surface_ids: HashMap<ObjectId, SurfaceIdWrapper>,
subsurface_state: Option<SubsurfaceState>,
surface_subsurfaces: HashMap<window::Id, Vec<SubsurfaceInstance>>,
popup_toplevels: HashMap<ObjectId, window::Id>,
}
impl PlatformSpecific {
@ -174,11 +173,6 @@ impl WaylandSpecific {
self.conn.as_ref()
}
/// Whether a popup opened from `toplevel` is alive.
pub(crate) fn has_popup(&self, toplevel: window::Id) -> bool {
self.popup_toplevels.values().any(|id| *id == toplevel)
}
pub(crate) async fn handle_event<'a, 'b, P>(
&mut self,
e: SctkEvent,
@ -207,33 +201,8 @@ impl WaylandSpecific {
modifiers,
subsurface_state,
surface_subsurfaces,
popup_toplevels,
} = self;
if let SctkEvent::PopupEvent {
variant,
parent_id,
parent_window,
id,
..
} = &e
{
match variant {
PopupEventVariant::Created(..) => {
// A nested popup's parent is itself a popup so inhreit its toplevel
let toplevel = popup_toplevels
.get(&parent_id.id())
.copied()
.unwrap_or(*parent_window);
_ = popup_toplevels.insert(id.id(), toplevel);
}
PopupEventVariant::Done => {
_ = popup_toplevels.remove(&id.id());
}
_ => {}
}
}
match e {
sctk_event => {
let Some(sender) = sender.as_ref() else {

View file

@ -198,10 +198,6 @@ pub enum SctkEvent {
SessionUnlocked,
SurfaceScaleFactorChanged(f64, WlSurface, window::Id),
Winit(WindowId, WindowEvent),
PopupModifiers {
toplevel: window::Id,
modifiers: cctk::sctk::seat::keyboard::Modifiers,
},
Subcompositor(SubsurfaceState),
ShortcutsInhibited(bool),
}
@ -627,12 +623,7 @@ impl SctkEvent {
keyboard::Event::KeyPressed {
key: key.clone(),
location: location,
// sctk reports keys without a text representation (e.g. arrows, Home) as
// Some("") but winit reports None. Widgets are written against winit's contract.
text: ke
.utf8
.filter(|s| !s.is_empty())
.map(|s| s.into()),
text: ke.utf8.map(|s| s.into()),
modifiers: modifiers_to_native(*modifiers),
physical_key,
repeat: false,
@ -658,9 +649,7 @@ impl SctkEvent {
keyboard::Event::KeyPressed {
key: key.clone(),
location: location,
text: utf8
.filter(|s| !s.is_empty())
.map(|s| s.into()),
text: utf8.map(|s| s.into()),
modifiers: modifiers_to_native(*modifiers),
physical_key,
repeat: true,
@ -1411,19 +1400,6 @@ impl SctkEvent {
),
)),
SctkEvent::Winit(_, _) => {}
SctkEvent::PopupModifiers {
toplevel,
modifiers,
} => {
events.push((
Some(toplevel),
iced_runtime::core::Event::Keyboard(
keyboard::Event::ModifiersChanged(modifiers_to_native(
modifiers,
)),
),
));
}
SctkEvent::SurfaceScaleFactorChanged(scale, _, id) => {
if let Some(w) = window_manager.get_mut(id) {
w.state.update_scale_factor(scale);