Refactored core event sources to use our own events too.

Calling then "raw-events", this removes the winit events from all public APIs and enable event faking by default.
This commit is contained in:
Samuel Guerra 2021-06-29 21:46:20 -03:00
parent 3853b971c2
commit eda29d2af4
12 changed files with 1272 additions and 432 deletions

View File

@ -1606,7 +1606,7 @@ impl TestApp {
}
pub fn just_release_alt(&mut self) {
self.app.on_keyboard_input(self.window_id, Key::LAlt, ElementState::Released);
self.app.on_keyboard_input(self.window_id, Key::LAlt, KeyState::Released);
self.app.update(false);
}

File diff suppressed because it is too large Load Diff

View File

@ -76,7 +76,7 @@
//! Focus information exists as metadata associated with a window frame. This metadata can be manually queried by
//! creating a [`FrameFocusInfo`] or directly from a widget info by using the [`WidgetInfoFocusExt`] extension methods.
use crate::app::{AppEventSender, AppExtension, DeviceEvent, DeviceId};
use crate::app::{AppEventSender, AppExtension};
use crate::context::*;
use crate::event::*;
use crate::gesture::{shortcut, ShortcutEvent};
@ -546,6 +546,8 @@ impl AppExtension for FocusManager {
ReturnFocusChangedEvent.notify(ctx.events, args);
}
}
} else if let Some(args) = crate::app::raw_device_events::KeyEvent.update(args) {
self.last_keyboard_event = args.timestamp;
}
if let Some(request) = request {
@ -569,12 +571,6 @@ impl AppExtension for FocusManager {
}
}
fn device_event(&mut self, _: &mut AppContext, _: DeviceId, event: &DeviceEvent) {
if let DeviceEvent::Key(_) = event {
self.last_keyboard_event = Instant::now();
}
}
fn new_frame_ready(&mut self, ctx: &mut AppContext, window_id: WindowId) {
let (focus, windows) = ctx.services.req_multi::<(Focus, Windows)>();

View File

@ -1,7 +1,7 @@
//! Aggregate events.
use super::{service::Service, units::LayoutPoint, WidgetId};
use crate::app::*;
use crate::app::{raw_events::RawWindowFocusEvent, *};
use crate::command::Command;
use crate::context::*;
use crate::event::*;
@ -10,7 +10,7 @@ use crate::keyboard::*;
use crate::mouse::*;
use crate::render::*;
use crate::var::RcVar;
use crate::window::{WindowEvent, WindowId, Windows};
use crate::window::{WindowId, Windows};
use std::num::NonZeroU32;
use std::{
convert::{TryFrom, TryInto},
@ -559,7 +559,7 @@ impl KeyInputArgs {
/// See also [`ShortcutArgs`].
#[inline]
pub fn gesture(&self) -> Option<KeyGesture> {
if self.state == ElementState::Released {
if self.state == KeyState::Released {
return None;
}
@ -718,9 +718,11 @@ impl AppExtension for GestureManager {
r.services.register(Gestures::new());
}
fn window_event(&mut self, _: &mut AppContext, _: WindowId, event: &WindowEvent) {
if let WindowEvent::Focused(false) = event {
self.pressed_modifier = None;
fn event_preview<EV: EventUpdateArgs>(&mut self, _: &mut AppContext, args: &EV) {
if let Some(args) = RawWindowFocusEvent.update(args) {
if !args.focused {
self.pressed_modifier = None;
}
}
}
@ -735,7 +737,7 @@ impl AppExtension for GestureManager {
if !args.stop_propagation_requested() {
if let Some(key) = args.key {
match args.state {
ElementState::Pressed => {
KeyState::Pressed => {
if let Ok(gesture_key) = GestureKey::try_from(key) {
let s_args = ShortcutArgs::new(
args.timestamp,
@ -755,7 +757,7 @@ impl AppExtension for GestureManager {
self.pressed_modifier = None;
}
}
ElementState::Released => {
KeyState::Released => {
if let Ok(mod_gesture) = ModifierGesture::try_from(key) {
if Some(mod_gesture) == self.pressed_modifier.take() && args.modifiers.is_empty() {
let s_args = ShortcutArgs::new(

View File

@ -4,32 +4,31 @@
//! is included in the [default app](crate::app::App::default) and provides the [`Keyboard`] service
//! and keyboard input events.
use crate::app::*;
use crate::app::{raw_events::*, *};
use crate::context::*;
use crate::event::*;
use crate::focus::{Focus, FocusExt};
use crate::focus::FocusExt;
use crate::render::WidgetPath;
use crate::service::*;
use crate::window::{WindowEvent, WindowId, Windows};
use crate::var::{RcVar, ReadOnlyRcVar, Var, Vars};
use crate::window::WindowId;
pub use glutin::event::{KeyboardInput, ModifiersState, ScanCode};
event_args! {
/// Keyboard event args.
/// Arguments for [`KeyInputEvent`].
pub struct KeyInputArgs {
/// Id of window that received the event.
/// Window that received the event.
pub window_id: WindowId,
/// Id of device that generated the event.
///
/// Is `None` if the event was generated programmatically.
pub device_id: Option<DeviceId>,
/// Device that generated the event.
pub device_id: DeviceId,
/// Raw code of key.
pub scan_code: ScanCode,
/// If the key was pressed or released.
pub state: ElementState,
pub state: KeyState,
/// Symbolic name of [`scan_code`](KeyInputArgs::scan_code).
pub key: Option<Key>,
@ -45,45 +44,18 @@ event_args! {
..
/// If the widget is focused or contains the focused widget.
/// Returns `true` if the widget is the [`target`](Self::target) or is a parent of the target.
fn concerns_widget(&self, ctx: &mut WidgetContext) -> bool {
self.target.contains(ctx.path.widget_id())
}
}
/// Raw keyboard event args.
pub struct RawKeyInputArgs {
/// Id of window that received the event.
pub window_id: WindowId,
/// Id of device that generated the event.
///
/// Is `None` if the event was generated programmatically.
pub device_id: Option<DeviceId>,
/// Raw code of key.
pub scan_code: ScanCode,
/// If the key was pressed or released.
pub state: ElementState,
/// Symbolic name of [`scan_code`](KeyInputArgs::scan_code).
pub key: Option<Key>,
..
/// Always `false`
fn concerns_widget(&self, _ctx: &mut WidgetContext) -> bool {
false
}
}
/// Character received event args.
/// Arguments for [`CharInputEvent`].
pub struct CharInputArgs {
/// Id of window that received the event.
/// Window that received the event.
pub window_id: WindowId,
/// The character.
/// Unicode character.
pub character: char,
/// The focused element at the time of the key input.
@ -91,13 +63,13 @@ event_args! {
..
/// If the widget is focused or contains the focused widget.
/// Returns `true` if the widget is the [`target`](Self::target) or is a parent of the target.
fn concerns_widget(&self, ctx: &mut WidgetContext) -> bool {
self.target.contains(ctx.path.widget_id())
}
}
/// Keyboard modifiers changed event args.
/// Arguments for [`ModifiersChangedEvent`].
pub struct ModifiersChangedArgs {
/// Previous modifiers state.
pub prev_modifiers: ModifiersState,
@ -105,35 +77,24 @@ event_args! {
/// Current modifiers state.
pub modifiers: ModifiersState,
/// The focused element at the time of the update.
pub target: WidgetPath,
..
/// If the widget is focused or contains the focused widget.
fn concerns_widget(&self, ctx: &mut WidgetContext) -> bool {
self.target.contains(ctx.path.widget_id())
/// Returns `true` for all widgets.
fn concerns_widget(&self, _ctx: &mut WidgetContext) -> bool {
true
}
}
}
event! {
/// Key pressed or released event.
/// Key pressed, repeat pressed or released event.
///
/// # Provider
///
/// This event is provided by the [`KeyboardManager`] extension.
pub KeyInputEvent: KeyInputArgs;
/// Key pressed or released without focus target.
/// You can invoke this event to fake a key-press that [`Keyboard`] accepts.
///
/// # Provider
///
/// This event is provided by the [`KeyboardManager`] extension.
pub RawKeyInputEvent: RawKeyInputArgs;
/// Key pressed or repeat event.
/// Key pressed or repeat pressed event.
///
/// # Provider
///
@ -147,7 +108,7 @@ event! {
/// This event is provided by the [`KeyboardManager`] extension.
pub KeyUpEvent: KeyInputArgs;
/// Modifiers state changed event.
/// Modifiers key state changed event.
///
/// # Provider
///
@ -162,7 +123,9 @@ event! {
pub CharInputEvent: CharInputArgs;
}
/// Application extension that provides keyboard events.
/// Application extension that provides keyboard events targeting the focused widget.
///
/// This [extension] processes the raw keyboard events retargeting then to the focused widget, generating derived events and variables.
///
/// # Events
///
@ -182,87 +145,38 @@ event! {
///
/// # Default
///
/// This extension is included in the [default app](crate::app::App::default), events provided by it
/// This extension is included in the [default app], events provided by it
/// are required by multiple other extensions.
///
/// # Dependencies
///
/// This extension requires the [`Focus`] and [`Windows`] services before the first window event. It does not
/// This extension requires the [`Focus`] and [`Windows`] services before the first raw key input event. It does not
/// require anything for initialization.
///
/// [extension]: AppExtension
/// [default app]: crate::app::App::default
/// [`Focus`]: crate::focus::Focus
/// [`Windows`]: crate::window::Windows
#[derive(Default)]
pub struct KeyboardManager;
impl KeyboardManager {
fn target(window_id: WindowId, services: &mut Services) -> Option<WidgetPath> {
if let Some(focused) = services.get::<Focus>().and_then(|f| f.focused().cloned()) {
Some(focused)
} else {
services
.get::<Windows>()
.and_then(|f| f.window(window_id).ok())
.map(|w| w.frame_info().root().path())
}
}
}
impl AppExtension for KeyboardManager {
fn init(&mut self, r: &mut AppContext) {
r.services.register(Keyboard::default());
}
fn window_event(&mut self, ctx: &mut AppContext, window_id: WindowId, event: &WindowEvent) {
match *event {
WindowEvent::KeyboardInput {
device_id,
input:
KeyboardInput {
scancode,
state,
virtual_keycode: key,
..
},
..
} => {
RawKeyInputEvent.notify(
ctx.events,
RawKeyInputArgs::now(window_id, Some(device_id), scancode, state, key.map(Into::into)),
);
}
WindowEvent::ModifiersChanged(m) => {
if let Some(target) = Self::target(window_id, ctx.services) {
ctx.services.keyboard().set_modifiers(m, target, ctx.events);
}
}
WindowEvent::ReceivedCharacter(c) => {
if let Some(target) = Self::target(window_id, ctx.services) {
ctx.services.keyboard().char_input(c, target, ctx.events);
}
}
_ => {}
}
}
fn event<EV: EventUpdateArgs>(&mut self, ctx: &mut AppContext, args: &EV) {
fn event_preview<EV: EventUpdateArgs>(&mut self, ctx: &mut AppContext, args: &EV) {
if let Some(args) = RawKeyInputEvent.update(args) {
if let Some(target) = ctx.services.focus().focused() {
let args = KeyInputArgs::new(
args.timestamp,
args.window_id,
args.device_id,
args.scan_code,
args.state,
args.key,
// TODO
ModifiersState::empty(),
false,
target.clone(),
);
KeyInputEvent.notify(ctx.events, args.clone());
match args.state {
ElementState::Pressed => KeyDownEvent.notify(ctx.events, args),
ElementState::Released => KeyUpEvent.notify(ctx.events, args),
let focused = ctx.services.focus().focused().cloned();
let keyboard = ctx.services.keyboard();
keyboard.key_input(ctx.events, ctx.vars, args, focused);
} else if let Some(args) = RawModifiersChangedEvent.update(args) {
let keyboard = ctx.services.keyboard();
keyboard.set_modifiers(ctx.events, ctx.vars, args.modifiers);
} else if let Some(args) = RawCharInputEvent.update(args) {
let focused = ctx.services.focus().focused().cloned();
if let Some(target) = focused {
if target.window_id() == args.window_id {
CharInputEvent.notify(ctx, CharInputArgs::now(args.window_id, args.character, target));
}
}
}
@ -277,76 +191,142 @@ impl AppExtension for KeyboardManager {
#[derive(Service, Default)]
pub struct Keyboard {
modifiers: ModifiersState,
last_key_down: Option<(Option<DeviceId>, ScanCode)>,
modifiers_var: RcVar<ModifiersState>,
codes: Vec<ScanCode>,
codes_var: RcVar<Vec<ScanCode>>,
keys: Vec<Key>,
keys_var: RcVar<Vec<Key>>,
last_key_down: Option<(DeviceId, ScanCode)>,
}
impl Keyboard {
/// Process a software keyboard input.
#[inline]
pub fn input(&mut self, key: Key, state: ElementState, target: WidgetPath, events: &mut Events) {
self.do_input(None, key as ScanCode, Some(key), state, target, events);
}
fn set_modifiers(&mut self, events: &mut Events, vars: &Vars, modifiers: ModifiersState) {
if self.modifiers_var.set_ne(vars, modifiers) {
let prev_modifiers = self.modifiers;
self.modifiers = modifiers;
/// Process a external keyboard input.
#[inline]
pub fn device_input(
&mut self,
device_id: DeviceId,
scan_code: ScanCode,
key: Option<Key>,
state: ElementState,
target: WidgetPath,
events: &mut Events,
) {
self.do_input(Some(device_id), scan_code, key, state, target, events);
}
/// Set the keyboard modifiers state.
pub fn set_modifiers(&mut self, modifiers: ModifiersState, target: WidgetPath, events: &mut Events) {
if self.modifiers != modifiers {
let prev_modifiers = std::mem::replace(&mut self.modifiers, modifiers);
let args = ModifiersChangedArgs::now(prev_modifiers, modifiers, target);
ModifiersChangedEvent.notify(events, args);
ModifiersChangedEvent.notify(events, ModifiersChangedArgs::now(prev_modifiers, modifiers));
}
}
/// Character input.
pub fn char_input(&mut self, character: char, target: WidgetPath, events: &mut Events) {
let args = CharInputArgs::now(target.window_id(), character, target);
CharInputEvent.notify(events, args);
fn key_input(&mut self, events: &mut Events, vars: &Vars, args: &RawKeyInputArgs, focused: Option<WidgetPath>) {
let mut repeat = false;
// update state and vars
match args.state {
KeyState::Pressed => {
repeat = self
.last_key_down
.map(|(d, s)| args.device_id == d && args.scan_code == s)
.unwrap_or_default();
if !repeat {
self.last_key_down = Some((args.device_id, args.scan_code));
}
if !self.codes.contains(&args.scan_code) {
self.codes.push(args.scan_code);
self.codes_var.set(vars, self.codes.clone());
}
if let Some(key) = args.key {
if !self.keys.contains(&key) {
self.keys.push(key);
self.keys_var.set(vars, self.keys.clone());
}
}
}
KeyState::Released => {
self.last_key_down = None;
if let Some(i) = self.codes.iter().position(|&c| c == args.scan_code) {
self.codes.swap_remove(i);
self.codes_var.set(vars, self.codes.clone());
}
if let Some(key) = args.key {
if let Some(i) = self.keys.iter().position(|&c| c == key) {
self.keys.swap_remove(i);
self.keys_var.set(vars, self.keys.clone());
}
}
}
}
// notify events
if let Some(target) = focused {
if target.window_id() == args.window_id {
let args = KeyInputArgs::now(
args.window_id,
args.device_id,
args.scan_code,
args.state,
args.key,
self.modifiers,
repeat,
target,
);
KeyInputEvent.notify(events, args.clone());
match args.state {
KeyState::Pressed => KeyDownEvent.notify(events, args),
KeyState::Released => KeyUpEvent.notify(events, args),
}
}
}
}
/// Current modifiers pressed.
/// Returns the currently pressed modifier keys.
#[inline]
pub fn modifiers(&self) -> ModifiersState {
self.modifiers
}
fn do_input(
&mut self,
device_id: Option<DeviceId>,
scan_code: ScanCode,
key: Option<Key>,
state: ElementState,
target: WidgetPath,
events: &mut Events,
) {
let mut repeat = false;
if state == ElementState::Pressed {
repeat = self.last_key_down == Some((device_id, scan_code));
if !repeat {
self.last_key_down = Some((device_id, scan_code));
}
} else {
self.last_key_down = None;
}
/// Returns a read-only variable that updates when the [`modifiers`](Self::modifiers) change.
#[inline]
pub fn modifiers_var(&self) -> ReadOnlyRcVar<ModifiersState> {
self.modifiers_var.clone().into_read_only()
}
let args = KeyInputArgs::now(target.window_id(), device_id, scan_code, state, key, self.modifiers, repeat, target);
/// Returns the [`ScanCode`] of the keys currently pressed.
#[inline]
pub fn codes(&self) -> &[ScanCode] {
&self.codes
}
KeyInputEvent.notify(events, args.clone());
/// Returns a read-only variable that updates when the [`codes`](Self::codes) change.
#[inline]
pub fn codes_var(&self) -> ReadOnlyRcVar<Vec<ScanCode>> {
self.codes_var.clone().into_read_only()
}
match args.state {
ElementState::Pressed => KeyDownEvent.notify(events, args),
ElementState::Released => KeyUpEvent.notify(events, args),
/// Returns the [`Key`] identifier of the keys currently pressed.
#[inline]
pub fn keys(&self) -> &[Key] {
&self.keys
}
/// Returns a read-only variable that updates when the [`keys`](Self::keys) change.
#[inline]
pub fn keys_var(&self) -> ReadOnlyRcVar<Vec<Key>> {
self.keys_var.clone().into_read_only()
}
}
/// State a keyboard [`Key`] has entered.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum KeyState {
/// The key was pressed.
Pressed,
/// The key was released.
Released,
}
impl From<glutin::event::ElementState> for KeyState {
fn from(s: glutin::event::ElementState) -> Self {
match s {
glutin::event::ElementState::Pressed => KeyState::Pressed,
glutin::event::ElementState::Released => KeyState::Released,
}
}
}
@ -774,10 +754,12 @@ impl From<Key> for VKey {
}
}
// TODO refactor this.
/// Extension trait that adds keyboard simulation methods to [`HeadlessApp`].
pub trait HeadlessAppKeyboardExt {
/// Does a keyboard input event.
fn on_keyboard_input(&mut self, window_id: WindowId, key: Key, state: ElementState);
fn on_keyboard_input(&mut self, window_id: WindowId, key: Key, state: KeyState);
/// Does a keyboard modifiers changed event.
fn on_modifiers_changed(&mut self, window_id: WindowId, modifiers: ModifiersState);
@ -789,13 +771,16 @@ pub trait HeadlessAppKeyboardExt {
fn press_modified_key(&mut self, window_id: WindowId, modifiers: ModifiersState, key: Key);
}
impl HeadlessAppKeyboardExt for HeadlessApp {
fn on_keyboard_input(&mut self, window_id: WindowId, key: Key, state: ElementState) {
fn on_keyboard_input(&mut self, window_id: WindowId, key: Key, state: KeyState) {
#[allow(deprecated)]
let raw_event = WindowEvent::KeyboardInput {
device_id: unsafe { DeviceId::dummy() },
let raw_event = glutin::event::WindowEvent::KeyboardInput {
device_id: unsafe { glutin::event::DeviceId::dummy() },
input: KeyboardInput {
scancode: 0,
state,
state: match state {
KeyState::Pressed => glutin::event::ElementState::Pressed,
KeyState::Released => glutin::event::ElementState::Released,
},
virtual_keycode: Some(key.into()),
// what can we
@ -808,13 +793,13 @@ impl HeadlessAppKeyboardExt for HeadlessApp {
}
fn on_modifiers_changed(&mut self, window_id: WindowId, modifiers: ModifiersState) {
let raw_event = WindowEvent::ModifiersChanged(modifiers);
let raw_event = glutin::event::WindowEvent::ModifiersChanged(modifiers);
self.window_event(window_id, &raw_event);
}
fn press_key(&mut self, window_id: WindowId, key: Key) {
self.on_keyboard_input(window_id, key, ElementState::Pressed);
self.on_keyboard_input(window_id, key, ElementState::Released);
self.on_keyboard_input(window_id, key, KeyState::Pressed);
self.on_keyboard_input(window_id, key, KeyState::Released);
self.update(false);
}
@ -823,40 +808,40 @@ impl HeadlessAppKeyboardExt for HeadlessApp {
self.press_key(window_id, key);
} else {
if modifiers.logo() {
self.on_keyboard_input(window_id, Key::LLogo, ElementState::Pressed);
self.on_keyboard_input(window_id, Key::LLogo, KeyState::Pressed);
}
if modifiers.ctrl() {
self.on_keyboard_input(window_id, Key::LCtrl, ElementState::Pressed);
self.on_keyboard_input(window_id, Key::LCtrl, KeyState::Pressed);
}
if modifiers.shift() {
self.on_keyboard_input(window_id, Key::LShift, ElementState::Pressed);
self.on_keyboard_input(window_id, Key::LShift, KeyState::Pressed);
}
if modifiers.alt() {
self.on_keyboard_input(window_id, Key::LAlt, ElementState::Pressed);
self.on_keyboard_input(window_id, Key::LAlt, KeyState::Pressed);
}
self.on_modifiers_changed(window_id, modifiers);
// pressed the modifiers.
self.update(false);
self.on_keyboard_input(window_id, key, ElementState::Pressed);
self.on_keyboard_input(window_id, key, ElementState::Released);
self.on_keyboard_input(window_id, key, KeyState::Pressed);
self.on_keyboard_input(window_id, key, KeyState::Released);
// pressed the key.
self.update(false);
self.on_modifiers_changed(window_id, ModifiersState::default());
if modifiers.logo() {
self.on_keyboard_input(window_id, Key::LLogo, ElementState::Released);
self.on_keyboard_input(window_id, Key::LLogo, KeyState::Released);
}
if modifiers.ctrl() {
self.on_keyboard_input(window_id, Key::LCtrl, ElementState::Released);
self.on_keyboard_input(window_id, Key::LCtrl, KeyState::Released);
}
if modifiers.shift() {
self.on_keyboard_input(window_id, Key::LShift, ElementState::Released);
self.on_keyboard_input(window_id, Key::LShift, KeyState::Released);
}
if modifiers.alt() {
self.on_keyboard_input(window_id, Key::LAlt, ElementState::Released);
self.on_keyboard_input(window_id, Key::LAlt, KeyState::Released);
}
// released the modifiers.

View File

@ -4,18 +4,16 @@
use super::units::{LayoutPoint, LayoutRect, LayoutSize};
use super::WidgetId;
use crate::app::*;
use crate::app::{raw_events::*, *};
use crate::context::*;
use crate::event::*;
use crate::keyboard::ModifiersState;
use crate::render::*;
use crate::service::*;
use crate::window::{WindowEvent, WindowId, Windows, WindowsExt};
use crate::window::{WindowId, Windows, WindowsExt};
use std::{fmt, time::*};
use std::{mem, num::NonZeroU8};
type WPos = glutin::dpi::PhysicalPosition<f64>;
pub use glutin::event::MouseButton;
event_args! {
@ -70,7 +68,7 @@ event_args! {
pub modifiers: ModifiersState,
/// The state the [`button`](MouseInputArgs::button) was changed to.
pub state: ElementState,
pub state: ButtonState,
/// Hit-test result for the mouse point in the window.
pub hits: FrameHitInfo,
@ -407,9 +405,25 @@ impl Default for MouseManager {
}
}
}
/// State a mouse button has entered.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ButtonState {
/// The button was pressed.
Pressed,
/// The button was released.
Released,
}
impl From<glutin::event::ElementState> for ButtonState {
fn from(s: glutin::event::ElementState) -> Self {
match s {
glutin::event::ElementState::Pressed => ButtonState::Pressed,
glutin::event::ElementState::Released => ButtonState::Released,
}
}
}
impl MouseManager {
fn on_mouse_input(&mut self, window_id: WindowId, device_id: DeviceId, state: ElementState, button: MouseButton, ctx: &mut AppContext) {
fn on_mouse_input(&mut self, window_id: WindowId, device_id: DeviceId, state: ButtonState, button: MouseButton, ctx: &mut AppContext) {
let position = if self.pos_window == Some(window_id) {
self.pos
} else {
@ -427,7 +441,7 @@ impl MouseManager {
(frame_info.root().path(), position)
};
if state == ElementState::Pressed {
if state == ButtonState::Pressed {
self.capture_count += 1;
if self.capture_count == 1 {
mouse.start_window_capture(target.clone(), ctx.events);
@ -465,7 +479,7 @@ impl MouseManager {
MouseInputEvent.notify(ctx.events, args.clone());
match state {
ElementState::Pressed => {
ButtonState::Pressed => {
// on_mouse_down
MouseDownEvent.notify(ctx.events, args);
@ -500,7 +514,7 @@ impl MouseManager {
}
self.last_pressed = now;
}
ElementState::Released => {
ButtonState::Released => {
// on_mouse_up
MouseUpEvent.notify(ctx.events, args);
@ -534,7 +548,7 @@ impl MouseManager {
}
}
fn on_cursor_moved(&mut self, window_id: WindowId, device_id: DeviceId, position: WPos, ctx: &mut AppContext) {
fn on_cursor_moved(&mut self, window_id: WindowId, device_id: DeviceId, position: (i32, i32), ctx: &mut AppContext) {
let mut moved = Some(window_id) != self.pos_window;
if moved {
@ -546,7 +560,7 @@ impl MouseManager {
self.pos_dpi = windows.window(window_id).unwrap().scale_factor();
}
let pos = LayoutPoint::new(position.x as f32 / self.pos_dpi, position.y as f32 / self.pos_dpi);
let pos = LayoutPoint::new(position.0 as f32 / self.pos_dpi, position.1 as f32 / self.pos_dpi);
moved |= pos != self.pos;
@ -705,17 +719,21 @@ impl AppExtension for MouseManager {
r.services.register(Mouse::new(r.updates.sender()));
}
fn window_event(&mut self, ctx: &mut AppContext, window_id: WindowId, event: &WindowEvent) {
match *event {
WindowEvent::CursorMoved { device_id, position, .. } => self.on_cursor_moved(window_id, device_id, position, ctx),
WindowEvent::MouseInput {
state, device_id, button, ..
} => self.on_mouse_input(window_id, device_id, state, button, ctx),
WindowEvent::ModifiersChanged(m) => self.modifiers = m,
WindowEvent::CursorLeft { device_id } => self.on_cursor_left(window_id, device_id, ctx),
WindowEvent::Focused(false) => self.on_window_blur(window_id, ctx),
WindowEvent::Destroyed => self.on_window_closed(window_id, ctx),
_ => {}
fn event_preview<EV: EventUpdateArgs>(&mut self, ctx: &mut AppContext, args: &EV) {
if let Some(args) = RawCursorMovedEvent.update(args) {
self.on_cursor_moved(args.window_id, args.device_id, args.position, ctx);
} else if let Some(args) = RawMouseInputEvent.update(args) {
self.on_mouse_input(args.window_id, args.device_id, args.state, args.button, ctx);
} else if let Some(args) = RawModifiersChangedEvent.update(args) {
self.modifiers = args.modifiers;
} else if let Some(args) = RawCursorLeftEvent.update(args) {
self.on_cursor_left(args.window_id, args.device_id, ctx);
} else if let Some(args) = RawWindowFocusEvent.update(args) {
if !args.focused {
self.on_window_blur(args.window_id, ctx);
}
} else if let Some(args) = RawWindowClosedEvent.update(args) {
self.on_window_closed(args.window_id, ctx);
}
}

View File

@ -109,7 +109,6 @@ impl std::fmt::Display for VarIsReadOnly {
/// A value type using [`IntoVar`] twice to support a shorthand initialization syntax:
///
/// ```
/// # fn main() { }
/// # use zero_ui_core::var::*;
/// # use zero_ui_core::*;
/// #[derive(Debug, Clone)]
@ -121,7 +120,7 @@ impl std::fmt::Display for VarIsReadOnly {
/// type Var = OwnedVar<Size>;
///
/// fn into_var(self) -> Self::Var {
/// OwnedVar(Size { width: self.0, height: self.1 })
/// OwnedVar(Size { width: self.0 as f32, height: self.1 as f32 })
/// }
/// }
/// impl IntoVar<Size> for (f32, f32) {
@ -134,31 +133,31 @@ impl std::fmt::Display for VarIsReadOnly {
/// #[property(size)]
/// pub fn size(child: impl UiNode, size: impl IntoVar<Size>) -> impl UiNode {
/// // ...
/// # todo!()
/// # child
/// }
/// # #[widget($crate::blank)]
/// # mod blank { }
///
/// # fn main() {
/// // shorthand #1:
/// blank! {
/// let wgt = blank! {
/// size = (800, 600);
/// }
/// };
///
/// // shorthand #2:
/// blank! {
/// let wgt = blank! {
/// size = (800.1, 600.2);
/// }
/// };
///
/// // blanket impl:
/// blank! {
/// let wgt = blank! {
/// size = Size { width: 800.0, height: 600.0 };
/// }
/// };
/// # }
/// ```
///
/// A property implemented using [`IntoVar`]:
///
/// ```
/// # fn main() { }
/// # use zero_ui_core::var::*;
/// # use zero_ui_core::text::*;
/// # use zero_ui_core::context::*;
@ -170,7 +169,7 @@ impl std::fmt::Display for VarIsReadOnly {
/// bar: V
/// }
/// #[impl_ui_node(child)]
/// impl<C: UiNode, V: Var<Text>> UiNode for FooNode<C, V> {
/// impl<C: UiNode, V: Var<u32>> UiNode for FooNode<C, V> {
/// fn init(&mut self, ctx: &mut WidgetContext) {
/// self.child.init(ctx);
/// println!("init: {}", self.bar.get(ctx));
@ -189,26 +188,27 @@ impl std::fmt::Display for VarIsReadOnly {
///
/// # #[widget($crate::blank)]
/// # pub mod blank { }
///
/// # fn main() {
/// // literal assign:
/// blank! {
/// let wgt = blank! {
/// foo = 42;
/// }
/// };
///
/// // variable assign:
/// let variable = var(42);
/// blank! {
/// let wgt = blank! {
/// foo = variable;
/// }
/// };
///
/// // widget when:
/// blank! {
/// let wgt = blank! {
/// foo = 42;
///
/// when !self.enabled {
/// foo = 32;
/// }
/// }
/// };
/// # }
/// ```
///
/// The property implementation is minimal and yet it supports a variety of different inputs that

View File

@ -502,3 +502,6 @@ impl<T: VarValue> ResponderVar<T> {
self.clone().into_read_only()
}
}
#[doc(hidden)]
pub type ReadOnlyRcVar<T> = ReadOnlyVar<T, RcVar<T>>;

View File

@ -1,6 +1,6 @@
//! App windows manager.
use crate::{
app::{self, AppEventSender, AppExtended, AppExtension, AppProcessExt, ShutdownRequestedArgs, WindowTarget},
app::{self, raw_events::*, AppEventSender, AppExtended, AppExtension, AppProcessExt, ShutdownRequestedArgs, WindowTarget},
context::*,
event::*,
profiler::profile_scope,
@ -22,7 +22,7 @@ use std::{
};
use webrender::api::{Epoch, PipelineId, RenderApi};
pub use glutin::{event::WindowEvent, window::CursorIcon};
pub use glutin::window::{CursorIcon, Theme as WindowTheme};
unique_id! {
/// Unique identifier of a headless window.
@ -203,16 +203,16 @@ impl HeadlessAppWindowExt for app::HeadlessApp {
if let Some(focused) = focused {
// blur_window
let event = WindowEvent::Focused(false);
let event = glutin::event::WindowEvent::Focused(false);
self.window_event(focused, &event);
}
let event = WindowEvent::Focused(true);
let event = glutin::event::WindowEvent::Focused(true);
self.window_event(window_id, &event);
self.update(false);
}
fn blur_window(&mut self, window_id: WindowId) {
let event = WindowEvent::Focused(false);
let event = glutin::event::WindowEvent::Focused(false);
self.window_event(window_id, &event);
self.update(false);
}
@ -249,7 +249,7 @@ impl HeadlessAppWindowExt for app::HeadlessApp {
}
fn close_window(&mut self, window_id: WindowId) -> bool {
let event = WindowEvent::CloseRequested;
let event = glutin::event::WindowEvent::CloseRequested;
self.window_event(window_id, &event);
let mut requested = false;
@ -456,82 +456,69 @@ impl AppExtension for WindowManager {
ctx.services.register(Windows::new(ctx.updates.sender()));
}
fn window_event(&mut self, ctx: &mut AppContext, window_id: WindowId, event: &WindowEvent) {
match event {
WindowEvent::Focused(focused) => {
if let Some(window) = ctx.services.windows().windows.iter_mut().find(|w| w.id == window_id) {
window.is_focused = *focused;
fn event_preview<EV: EventUpdateArgs>(&mut self, ctx: &mut AppContext, args: &EV) {
if let Some(args) = RawWindowFocusEvent.update(args) {
if let Some(window) = ctx.services.windows().windows.iter_mut().find(|w| w.id == args.window_id) {
window.is_focused = args.focused;
let args = WindowIsFocusedArgs::now(window_id, window.is_focused, false);
self.notify_focus(args, ctx.events);
let args = WindowIsFocusedArgs::now(args.window_id, window.is_focused, false);
self.notify_focus(args, ctx.events);
}
} else if let Some(args) = RawWindowResizedEvent.update(args) {
if let Some(window) = ctx.services.windows().windows.iter_mut().find(|w| w.id == args.window_id) {
let new_size = window.size();
// set the window size variable.
if window.vars.size().set_ne(ctx.vars, new_size) {
// is new size:
ctx.updates.layout();
window.expect_layout_update();
window.resize_renderer();
// raise window_resize
WindowResizeEvent.notify(ctx.events, WindowResizeArgs::now(args.window_id, new_size));
}
}
WindowEvent::Resized(_) => {
if let Some(window) = ctx.services.windows().windows.iter_mut().find(|w| w.id == window_id) {
let new_size = window.size();
} else if let Some(args) = RawWindowMovedEvent.update(args) {
if let Some(window) = ctx.services.windows().windows.iter().find(|w| w.id == args.window_id) {
let new_position = window.position();
// set the window size variable.
if window.vars.size().set_ne(ctx.vars, new_size) {
// is new size:
ctx.updates.layout();
window.expect_layout_update();
window.resize_renderer();
// TODO check if in new monitor.
// raise window_resize
WindowResizeEvent.notify(ctx.events, WindowResizeArgs::now(window_id, new_size));
// set the window position variable if it is not read-only.
window.vars.position().set_ne(ctx.vars, new_position);
// raise window_move
WindowMoveEvent.notify(ctx.events, WindowMoveArgs::now(args.window_id, new_position));
}
} else if let Some(args) = RawWindowCloseRequestedEvent.update(args) {
if let Some(win) = ctx.services.windows().windows.iter().find(|w| w.id == args.window_id) {
*win.close_response.borrow_mut() = Some(response_var().0);
ctx.updates.update();
}
} else if let Some(args) = RawWindowScaleFactorChangedEvent.update(args) {
if let Some(window) = ctx.services.windows().windows.iter_mut().find(|w| w.id == args.window_id) {
let scale_factor = args.scale_factor as f32;
let new_size = LayoutSize::new(args.size.0 as f32 / scale_factor, args.size.1 as f32 / scale_factor);
// winit has not set the new_inner_size yet, so
// we can determinate if the system only changed the size
// to visually match the new scale_factor or if the window was
// really resized.
if window.vars.size().copy(ctx.vars) == new_size.into() {
// if it only changed to visually match, the WindowEvent::Resized
// will not cause a re-layout, so we need to do it here, but window.resize_renderer()
// calls window.size(), so we need to set the new_inner_size before winit.
if let Some(w) = &window.window {
w.set_inner_size(glutin::dpi::PhysicalSize::new(args.size.0, args.size.1));
}
ctx.updates.layout();
window.expect_layout_update();
window.resize_renderer();
}
WindowScaleChangedEvent.notify(ctx.events, WindowScaleChangedArgs::now(args.window_id, scale_factor, new_size));
}
WindowEvent::Moved(_) => {
if let Some(window) = ctx.services.windows().windows.iter().find(|w| w.id == window_id) {
let new_position = window.position();
// TODO check if in new monitor.
// set the window position variable if it is not read-only.
window.vars.position().set_ne(ctx.vars, new_position);
// raise window_move
WindowMoveEvent.notify(ctx.events, WindowMoveArgs::now(window_id, new_position));
}
}
WindowEvent::CloseRequested => {
if let Some(win) = ctx.services.windows().windows.iter().find(|w| w.id == window_id) {
*win.close_response.borrow_mut() = Some(response_var().0);
ctx.updates.update();
}
}
WindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size,
} => {
if let Some(window) = ctx.services.windows().windows.iter_mut().find(|w| w.id == window_id) {
let scale_factor = *scale_factor as f32;
let new_size = LayoutSize::new(
new_inner_size.width as f32 / scale_factor,
new_inner_size.height as f32 / scale_factor,
);
// winit has not set the new_inner_size yet, so
// we can determinate if the system only changed the size
// to visually match the new scale_factor or if the window was
// really resized.
if window.vars.size().copy(ctx.vars) == new_size.into() {
// if it only changed to visually match, the WindowEvent::Resized
// will not cause a re-layout, so we need to do it here, but window.resize_renderer()
// calls window.size(), so we need to set the new_inner_size before winit.
if let Some(w) = &window.window {
w.set_inner_size(**new_inner_size);
}
ctx.updates.layout();
window.expect_layout_update();
window.resize_renderer();
}
WindowScaleChangedEvent.notify(ctx.events, WindowScaleChangedArgs::now(window_id, scale_factor, new_size));
}
}
_ => {}
}
}
@ -2587,13 +2574,13 @@ impl OpenWindow {
/// Sets a window subclass that calls a raw event handler.
///
/// Use this to receive Windows OS events not covered in [`WindowEvent`].
/// Use this to receive Windows OS events not covered in [`raw_events`].
///
/// Returns if adding a subclass handler succeeded.
///
/// # Handler
///
/// The handler inputs are the first 4 arguments of a [`SUBCLASSPROC`](https://docs.microsoft.com/en-us/windows/win32/api/commctrl/nc-commctrl-subclassproc).
/// The handler inputs are the first 4 arguments of a [`SUBCLASSPROC`].
/// You can use closure capture to include extra data.
///
/// The handler must return `Some(LRESULT)` to stop the propagation of a specific message.
@ -2603,6 +2590,9 @@ impl OpenWindow {
/// # Panics
///
/// Panics in headless mode.
///
/// [`raw_events`]: crate::app::raw_events
/// [`SUBCLASSPROC`]: https://docs.microsoft.com/en-us/windows/win32/api/commctrl/nc-commctrl-subclassproc
pub fn set_raw_windows_event_handler<
H: FnMut(
winapi::shared::windef::HWND,

View File

@ -63,6 +63,7 @@
//! content = text("Click Me!");
//! font_size = 28;
//! }
//! # ;
//! ```
//!
//! The example demonstrates the [`button!`] widget, you may thing the [`on_click`] and [`font_size`] are implemented in the widget,
@ -114,7 +115,7 @@
//!
//! #[widget($crate::red_button)]
//! mod red_button {
// use super::*;
//! use super::*;
//! inherit!(zero_ui::widgets::button);
//!
//! properties! {
@ -262,10 +263,10 @@
//! let moving_btn = button! {
//! margin = offset.clone();
//! on_click = hn!(|ctx, _| {
//! offset.modify(ctx, |m|n.left += 50.0);
//! offset.modify(ctx, |m|m.left += 50.0);
//! });
//! content = text("Click to Move!")
//! }
//! };
//! ```
//!
//! The button moves to the right when clicked, the `margin` starts at `10` and every click the variable is modified, this causes
@ -312,7 +313,7 @@
//! // 3 methods doing the same thing.
//! flag.set(ctx.vars, new_value);
//! flag.set_ne(ctx.vars, new_value);
//! flag.modify(ctx.vars, |f| *f = new_value);
//! flag.modify(ctx.vars, |f| **f = new_value);
//! });
//! };
//! ```
@ -367,7 +368,7 @@
//! content = button! {
//! content = text(count_text);
//! on_click = hn!(|ctx, _| {
//! count.modify(ctx, |c| *c += 1);
//! count.modify(ctx, |c| **c += 1);
//! });
//! }
//! }
@ -388,7 +389,7 @@
//!
//! ```
//! # use zero_ui::prelude::*;
//! #[derive(Clone, Copy, Debug)]
//! #[derive(Clone, Debug)]
//! enum Status {
//! Idle,
//! Info(Text)
@ -1397,7 +1398,7 @@ pub mod widgets;
pub mod prelude {
#[doc(no_inline)]
pub use crate::core::{
app::{App, ElementState},
app::App,
async_clone_move,
border::{BorderSides, BorderStyle, LineOrientation},
clone_move,
@ -1412,8 +1413,8 @@ pub mod prelude {
gesture::{shortcut, ClickArgs, CommandShortcutExt, GestureKey, Shortcut, ShortcutArgs, Shortcuts},
gradient::{stops, ExtendMode, GradientStop, GradientStops},
handler::*,
keyboard::{CharInputArgs, Key, KeyInputArgs, ModifiersChangedArgs, ModifiersState},
mouse::{MouseButton, MouseMoveArgs},
keyboard::{CharInputArgs, Key, KeyInputArgs, KeyState, ModifiersChangedArgs, ModifiersState},
mouse::{ButtonState, MouseButton, MouseMoveArgs},
node_vec, nodes,
render::WidgetPath,
service::Services,
@ -1498,8 +1499,6 @@ pub mod prelude {
/// }
/// ```
pub mod new_property {
#[doc(no_inline)]
pub use crate::core::app::ElementState;
#[doc(no_inline)]
pub use crate::core::border::*;
#[doc(no_inline)]
@ -1513,6 +1512,10 @@ pub mod prelude {
#[doc(no_inline)]
pub use crate::core::handler::*;
#[doc(no_inline)]
pub use crate::core::keyboard::KeyState;
#[doc(no_inline)]
pub use crate::core::mouse::ButtonState;
#[doc(no_inline)]
pub use crate::core::render::*;
#[doc(no_inline)]
pub use crate::core::task::{self, AppTask, WidgetTask};

View File

@ -169,12 +169,12 @@ pub fn is_pressed(child: impl UiNode, state: StateVar) -> impl UiNode {
} else if let Some(args) = MouseInputEvent.update(args) {
if IsEnabled::get(ctx) && args.is_primary() {
match args.state {
ElementState::Pressed => {
ButtonState::Pressed => {
if args.concerns_capture(ctx) {
self.is_down = true;
}
}
ElementState::Released => {
ButtonState::Released => {
self.is_down = false;
}
}
@ -254,12 +254,12 @@ pub fn is_cap_pressed(child: impl UiNode, state: StateVar) -> impl UiNode {
if let Some(args) = MouseInputEvent.update(args) {
if IsEnabled::get(ctx) && args.is_primary() {
match args.state {
ElementState::Pressed => {
ButtonState::Pressed => {
if args.concerns_capture(ctx) {
self.is_down = true;
}
}
ElementState::Released => {
ButtonState::Released => {
self.is_down = false;
}
}

View File

@ -288,8 +288,9 @@ pub mod window {
#[cfg(windows)]
fn setup_alt_f4_block(&self, ctx: &mut WidgetContext, opened_window: WindowId) {
use zero_ui_core::{
app::ElementState,
keyboard::{Key, RawKeyInputArgs, RawKeyInputEvent},
app::raw_events::{RawKeyInputArgs, RawKeyInputEvent},
app::DeviceId,
keyboard::{Key, KeyState},
};
let window_id = ctx.path.window_id();
@ -298,17 +299,18 @@ pub mod window {
}
let sender = ctx.events.sender(RawKeyInputEvent);
let sender_device_id = DeviceId::new_unique();
let window = ctx.services.windows().window(window_id).unwrap();
if let WindowMode::Headed = window.mode() {
let allow_alt_f4 = self.allow_alt_f4.clone();
window.set_raw_windows_event_handler(move |_, msg, wparam, _| {
if msg == winapi::um::winuser::WM_SYSKEYDOWN && wparam as i32 == winapi::um::winuser::VK_F4 && !allow_alt_f4.get() {
sender.send(RawKeyInputArgs::now(
let _ = sender.send(RawKeyInputArgs::now(
window_id,
None,
sender_device_id,
wparam as u32,
ElementState::Pressed,
KeyState::Pressed,
Some(Key::F4),
));
return Some(0);