1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Toolkit interface
//!
//! In KAS, the "toolkit" is an external library handling system interfaces
//! (windowing and event translation) plus rendering. This allows KAS's core
//! to remain system-neutral.
//!
//! Note: although the choice of windowing library is left to the toolkit, for
//! convenience KAS is able to use several [winit] types.
//!
//! [winit]: https://github.com/rust-windowing/winit

use std::num::NonZeroU32;

use crate::string::{CowString, CowStringL};
use crate::{event, ThemeAction, ThemeApi};

/// Identifier for a window or pop-up
///
/// Identifiers should always be unique.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct WindowId(NonZeroU32);

impl WindowId {
    /// Construct a [`WindowId`]
    ///
    /// Only for toolkit use!
    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
    pub fn new(n: NonZeroU32) -> WindowId {
        WindowId(n)
    }
}

/// Action required after processing
///
/// This type is returned by many widgets on modification to self and is tracked
/// internally by [`event::Manager`] to determine which updates are needed to
/// the UI.
///
/// All variants are *progressive*: e.g. `Reconfigure` implies all actions
/// needed to handle `Popup`, `RegionMoved` and `Redraw`.
///
/// Two `TkAction` values may be combined by taking their maximum. Since this
/// is a common operation, the `+` operator is defined to do this job, together
/// with `+=` on `TkAction` and [`event::Manager`].
///
/// Users receiving a value of this type from a widget update method should
/// generally call `*mgr += action;` during event handling. Prior to
/// starting the event loop (`toolkit.run()`), these values can be ignored.
#[must_use]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub enum TkAction {
    /// No action needed
    None,
    /// Whole window requires redrawing
    ///
    /// Note that [`Manager::redraw`] can instead be used for more selective
    /// redrawing, if supported by the toolkit.
    ///
    /// [`Manager::redraw`]: crate::event::Manager::redraw
    Redraw,
    /// Some widgets within a region moved
    ///
    /// This action should be emitted when e.g. a scroll-region is moved or
    /// widget layout is adjusted to allow for the fact that coordinates
    /// (e.g. mouse position) have changed relative to widgets.
    // NOTE: one could specify a Rect here, but there's not much advantage
    RegionMoved,
    /// A pop-up opened/closed/needs resizing
    Popup,
    /// Whole window requires reconfiguring
    ///
    /// *Configuring* widgets assigns [`WidgetId`] identifiers, updates
    /// [`event::Manager`] state and resizes all widgets.
    ///
    /// [`WidgetId`]: crate::WidgetId
    /// [`event::Manager`]: crate::event::Manager
    Reconfigure,
    /// The window or pop-up should be closed
    Close,
    /// All windows should close (toolkit exit)
    CloseAll,
}

impl std::ops::Add for TkAction {
    type Output = Self;

    #[inline]
    fn add(self, rhs: TkAction) -> Self {
        self.max(rhs)
    }
}

impl std::ops::AddAssign for TkAction {
    #[inline]
    fn add_assign(&mut self, rhs: TkAction) {
        *self = (*self).max(rhs);
    }
}

/// Toolkit-specific window management and style interface.
///
/// This is implemented by a KAS toolkit on a window handle.
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
pub trait TkWindow {
    /// Add a pop-up
    ///
    /// A pop-up may be presented as an overlay layer in the current window or
    /// via a new borderless window.
    ///
    /// Pop-ups support position hints: they are placed *next to* the specified
    /// `rect`, preferably in the given `direction`.
    fn add_popup(&mut self, popup: kas::Popup) -> WindowId;

    /// Add a window
    ///
    /// Toolkits typically allow windows to be added directly, before start of
    /// the event loop (e.g. `kas_wgpu::Toolkit::add`).
    ///
    /// This method is an alternative allowing a window to be added via event
    /// processing, albeit without error handling.
    fn add_window(&mut self, widget: Box<dyn kas::Window>) -> WindowId;

    /// Close a window
    fn close_window(&mut self, id: WindowId);

    /// Updates all subscribed widgets
    ///
    /// All widgets subscribed to the given [`event::UpdateHandle`], across all
    /// windows, will receive an update.
    fn trigger_update(&mut self, handle: event::UpdateHandle, payload: u64);

    /// Attempt to get clipboard contents
    ///
    /// In case of failure, paste actions will simply fail. The implementation
    /// may wish to log an appropriate warning message.
    fn get_clipboard(&mut self) -> Option<CowString>;

    /// Attempt to set clipboard contents
    fn set_clipboard<'c>(&mut self, content: CowStringL<'c>);

    /// Adjust the theme
    fn adjust_theme(&mut self, f: &mut dyn FnMut(&mut dyn ThemeApi) -> ThemeAction);

    /// Set the mouse cursor
    fn set_cursor_icon(&mut self, icon: event::CursorIcon);
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn action_precedence() {
        assert!(TkAction::None < TkAction::Redraw);
        assert!(TkAction::Redraw < TkAction::Reconfigure);
        assert!(TkAction::Reconfigure < TkAction::Close);
        assert!(TkAction::Close < TkAction::CloseAll);
    }
}