Compare commits
No commits in common. "24da1dc0e4e5c1b7f803bf8584d10939fe5acdec" and "d12bb8e056712ab87efbdd4296261331772ec413" have entirely different histories.
24da1dc0e4
...
d12bb8e056
23 changed files with 1228 additions and 1599 deletions
2119
Cargo.lock
generated
2119
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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::*;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -392,7 +392,6 @@ impl SctkEventLoop {
|
|||
).ok(),
|
||||
text_input_manager: TextInputManager::try_new(®istry_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(),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue