Skip to content

Commit

Permalink
Merge pull request #412 from kas-gui/work2
Browse files Browse the repository at this point in the history
Cleaner handling of pendling events; update Winit
  • Loading branch information
dhardy committed Sep 29, 2023
2 parents 1e84d2d + 5ae0165 commit 08db62d
Show file tree
Hide file tree
Showing 27 changed files with 894 additions and 716 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,7 @@ members = [
"crates/kas-view",
"examples/mandlebrot",
]

[patch.crates-io.winit]
git = "https://github.com/rust-windowing/winit.git"
rev = "cb58c49a90f17734e0405627130674d47c0b8f40"
4 changes: 0 additions & 4 deletions crates/kas-core/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ bitflags! {
///
/// Implies window redraw.
const REGION_MOVED = 1 << 4;
/*
/// A pop-up opened/closed/needs resizing
Popup,
*/
/// Reset size of all widgets without recalculating requirements
const SET_RECT = 1 << 8;
/// Resize all widgets in the window
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/core/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ pub trait Events: Widget + Sized {
///
/// Note: to implement `hover_highlight`, simply request a redraw on
/// focus gain and loss. To implement `cursor_icon`, call
/// `cx.set_cursor_icon(EXPR);` on focus gain.
/// `cx.set_hover_cursor(EXPR);` on focus gain.
///
/// [`#widget`]: macros::widget
#[inline]
Expand Down
14 changes: 2 additions & 12 deletions crates/kas-core/src/draw/draw_shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use super::color::Rgba;
use super::{DrawImpl, PassId};
use crate::cast::Cast;
use crate::geom::{Quad, Rect, Size};
use crate::shell::Platform;
use crate::text::{Effect, TextDisplay};
use crate::theme::RasterConfig;
use std::any::Any;
Expand Down Expand Up @@ -76,25 +75,21 @@ pub struct AllocError;
pub struct SharedState<DS: DrawSharedImpl> {
/// The shell's [`DrawSharedImpl`] object
pub draw: DS,
platform: Platform,
}

#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
impl<DS: DrawSharedImpl> SharedState<DS> {
/// Construct (this is only called by the shell)
pub fn new(draw: DS, platform: Platform) -> Self {
SharedState { draw, platform }
pub fn new(draw: DS) -> Self {
SharedState { draw }
}
}

/// Interface over [`SharedState`]
///
/// All methods concern management of resources for drawing.
pub trait DrawShared {
/// Get the platform
fn platform(&self) -> Platform;

/// Allocate an image
///
/// Use [`SharedState::image_upload`] to set contents of the new image.
Expand All @@ -121,11 +116,6 @@ pub trait DrawShared {
}

impl<DS: DrawSharedImpl> DrawShared for SharedState<DS> {
#[inline]
fn platform(&self) -> Platform {
self.platform
}

#[inline]
fn image_alloc(&mut self, size: (u32, u32)) -> Result<ImageHandle, AllocError> {
self.draw
Expand Down
18 changes: 2 additions & 16 deletions crates/kas-core/src/event/cx/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
//! Configuration context

use super::Pending;
use crate::draw::DrawShared;
use crate::event::EventState;
use crate::geom::{Rect, Size};
use crate::layout::AlignPair;
use crate::shell::Platform;
use crate::text::TextApi;
use crate::theme::{Feature, SizeCx, TextClass, ThemeSize};
use crate::{Action, Node, WidgetId};
Expand All @@ -26,21 +24,15 @@ use std::ops::{Deref, DerefMut};
#[must_use]
pub struct ConfigCx<'a> {
sh: &'a dyn ThemeSize,
ds: &'a mut dyn DrawShared,
pub(crate) ev: &'a mut EventState,
}

impl<'a> ConfigCx<'a> {
/// Construct
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
pub fn new(sh: &'a dyn ThemeSize, ds: &'a mut dyn DrawShared, ev: &'a mut EventState) -> Self {
ConfigCx { sh, ds, ev }
}

/// Get the platform
pub fn platform(&self) -> Platform {
self.ds.platform()
pub fn new(sh: &'a dyn ThemeSize, ev: &'a mut EventState) -> Self {
ConfigCx { sh, ev }
}

/// Access a [`SizeCx`]
Expand All @@ -49,12 +41,6 @@ impl<'a> ConfigCx<'a> {
SizeCx::new(self.sh)
}

/// Access [`DrawShared`]
#[inline]
pub fn draw_shared(&mut self) -> &mut dyn DrawShared {
self.ds
}

/// Access [`EventState`]
#[inline]
pub fn ev_state(&mut self) -> &mut EventState {
Expand Down
111 changes: 68 additions & 43 deletions crates/kas-core/src/event/cx/cx_pub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use crate::draw::DrawShared;
use crate::event::config::ChangeConfig;
use crate::geom::{Offset, Vec2};
use crate::theme::{SizeCx, ThemeControl};
#[cfg(all(wayland_platform, feature = "clipboard"))]
use crate::util::warn_about_error;
use crate::{Action, Erased, WidgetId, Window, WindowId};
#[allow(unused)] use crate::{Events, Layout}; // for doc-links

Expand All @@ -34,6 +36,11 @@ impl std::ops::BitOrAssign<Action> for EventState {

/// Public API
impl EventState {
/// Get the platform
pub fn platform(&self) -> Platform {
self.platform
}

/// True when the window has focus
#[inline]
pub fn window_has_focus(&self) -> bool {
Expand Down Expand Up @@ -526,8 +533,9 @@ impl EventState {
/// cases, calling this method may be ineffective. The cursor is
/// automatically "unset" when the widget is no longer hovered.
///
/// If a mouse grab ([`Press::grab`]) is active, its icon takes precedence.
pub fn set_cursor_icon(&mut self, icon: CursorIcon) {
/// See also [`Self::update_grab_cursor`]: if a mouse grab
/// ([`Press::grab`]) is active, its icon takes precedence.
pub fn set_hover_cursor(&mut self, icon: CursorIcon) {
// Note: this is acted on by EventState::update
self.hover_icon = icon;
}
Expand Down Expand Up @@ -601,11 +609,6 @@ impl EventState {
pub fn request_update(&mut self, id: WidgetId) {
self.pending.push_back(Pending::Update(id));
}

/// Request set_rect of the given path
pub fn request_set_rect(&mut self, id: WidgetId) {
self.pending.push_back(Pending::SetRect(id));
}
}

/// Public API
Expand All @@ -617,7 +620,7 @@ impl<'a> EventCx<'a> {
/// This is a shortcut to [`ConfigCx::configure`].
#[inline]
pub fn configure(&mut self, mut widget: Node<'_>, id: WidgetId) {
self.config_cx(|cx| widget._configure(cx, id));
widget._configure(&mut self.config_cx(), id);
}

/// Update a widget
Expand All @@ -627,7 +630,7 @@ impl<'a> EventCx<'a> {
/// data, it should call this (or [`ConfigCx::update`]) after mutating the state.
#[inline]
pub fn update(&mut self, mut widget: Node<'_>) {
self.config_cx(|cx| widget._update(cx));
widget._update(&mut self.config_cx());
}

/// Get the index of the last child visited
Expand Down Expand Up @@ -735,7 +738,8 @@ impl<'a> EventCx<'a> {
pub(crate) fn add_popup(&mut self, popup: crate::PopupDescriptor) -> WindowId {
log::trace!(target: "kas_core::event", "add_popup: {popup:?}");

let id = self.shell.add_popup(popup.clone());
let parent_id = self.window.window_id();
let id = self.shell.add_popup(parent_id, popup.clone());
let nav_focus = self.nav_focus.clone();
self.popups.push((id, popup, nav_focus));
self.clear_nav_focus();
Expand Down Expand Up @@ -791,7 +795,7 @@ impl<'a> EventCx<'a> {
/// This calls [`winit::window::Window::drag_window`](https://docs.rs/winit/latest/winit/window/struct.Window.html#method.drag_window). Errors are ignored.
pub fn drag_window(&self) {
#[cfg(winit)]
if let Some(ww) = self.shell.winit_window() {
if let Some(ww) = self.window.winit_window() {
if let Err(e) = ww.drag_window() {
log::warn!("EventCx::drag_window: {e}");
}
Expand All @@ -803,7 +807,7 @@ impl<'a> EventCx<'a> {
/// This calls [`winit::window::Window::drag_resize_window`](https://docs.rs/winit/latest/winit/window/struct.Window.html#method.drag_resize_window). Errors are ignored.
pub fn drag_resize_window(&self, direction: ResizeDirection) {
#[cfg(winit)]
if let Some(ww) = self.shell.winit_window() {
if let Some(ww) = self.window.winit_window() {
if let Err(e) = ww.drag_resize_window(direction) {
log::warn!("EventCx::drag_resize_window: {e}");
}
Expand All @@ -816,12 +820,29 @@ impl<'a> EventCx<'a> {
/// may wish to log an appropriate warning message.
#[inline]
pub fn get_clipboard(&mut self) -> Option<String> {
#[cfg(all(wayland_platform, feature = "clipboard"))]
if let Some(cb) = self.window.wayland_clipboard() {
return match cb.load() {
Ok(s) => Some(s),
Err(e) => {
warn_about_error("Failed to get clipboard contents", &e);
None
}
};
}

self.shell.get_clipboard()
}

/// Attempt to set clipboard contents
#[inline]
pub fn set_clipboard(&mut self, content: String) {
#[cfg(all(wayland_platform, feature = "clipboard"))]
if let Some(cb) = self.window.wayland_clipboard() {
cb.store(content);
return;
}

self.shell.set_clipboard(content)
}

Expand All @@ -831,6 +852,17 @@ impl<'a> EventCx<'a> {
/// paste on middle-click. This method does nothing on other platforms.
#[inline]
pub fn get_primary(&mut self) -> Option<String> {
#[cfg(all(wayland_platform, feature = "clipboard"))]
if let Some(cb) = self.window.wayland_clipboard() {
return match cb.load_primary() {
Ok(s) => Some(s),
Err(e) => {
warn_about_error("Failed to get clipboard contents", &e);
None
}
};
}

self.shell.get_primary()
}

Expand All @@ -840,6 +872,12 @@ impl<'a> EventCx<'a> {
/// paste on middle-click. This method does nothing on other platforms.
#[inline]
pub fn set_primary(&mut self, content: String) {
#[cfg(all(wayland_platform, feature = "clipboard"))]
if let Some(cb) = self.window.wayland_clipboard() {
cb.store_primary(content);
return;
}

self.shell.set_primary(content)
}

Expand All @@ -849,57 +887,44 @@ impl<'a> EventCx<'a> {
self.shell.adjust_theme(Box::new(f));
}

/// Access a [`SizeCx`]
/// Get a [`SizeCx`]
///
/// Warning: sizes are calculated using the window's current scale factor.
/// This may change, even without user action, since some platforms
/// always initialize windows with scale factor 1.
/// See also notes on [`Events::configure`].
pub fn size_cx<F: FnOnce(SizeCx) -> T, T>(&mut self, f: F) -> T {
let mut result = None;
self.shell.size_and_draw_shared(Box::new(|size, _| {
result = Some(f(SizeCx::new(size)));
}));
result.expect("ShellWindow::size_and_draw_shared impl failed to call function argument")
}

/// Access a [`ConfigCx`]
pub fn config_cx<F: FnOnce(&mut ConfigCx) -> T, T>(&mut self, f: F) -> T {
let mut result = None;
self.shell
.size_and_draw_shared(Box::new(|size, draw_shared| {
let mut cx = ConfigCx::new(size, draw_shared, self.state);
result = Some(f(&mut cx));
}));
result.expect("ShellWindow::size_and_draw_shared impl failed to call function argument")
}

/// Access a [`DrawShared`]
pub fn draw_shared<F: FnOnce(&mut dyn DrawShared) -> T, T>(&mut self, f: F) -> T {
let mut result = None;
self.shell.size_and_draw_shared(Box::new(|_, draw_shared| {
result = Some(f(draw_shared));
}));
result.expect("ShellWindow::size_and_draw_shared impl failed to call function argument")
pub fn size_cx(&self) -> SizeCx<'_> {
SizeCx::new(self.window.theme_size())
}

/// Get a [`ConfigCx`]
pub fn config_cx(&mut self) -> ConfigCx<'_> {
let size = self.window.theme_size();
ConfigCx::new(size, self.state)
}

/// Get a [`DrawShared`]
pub fn draw_shared(&mut self) -> &mut dyn DrawShared {
self.shell.draw_shared()
}

/// Directly access Winit Window
///
/// This is a temporary API, allowing e.g. to minimize the window.
#[cfg(winit)]
pub fn winit_window(&self) -> Option<&winit::window::Window> {
self.shell.winit_window()
self.window.winit_window()
}

/// Update the mouse cursor used during a grab
///
/// This only succeeds if widget `id` has an active mouse-grab (see
/// [`Press::grab`]). The cursor will be reset when the mouse-grab
/// ends.
pub fn update_grab_cursor(&mut self, id: WidgetId, icon: CursorIcon) {
pub fn set_grab_cursor(&mut self, id: &WidgetId, icon: CursorIcon) {
if let Some(ref grab) = self.mouse_grab {
if grab.start_id == id {
self.shell.set_cursor_icon(icon);
if grab.start_id == *id {
self.window.set_cursor_icon(icon);
}
}
}
Expand Down
Loading

0 comments on commit 08db62d

Please sign in to comment.