use epaint::ColorImage;
use crate::{emath::*, ViewportId, ViewportIdMap};
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct RawInput {
pub viewport_id: ViewportId,
pub viewports: ViewportIdMap<ViewportInfo>,
pub screen_rect: Option<Rect>,
pub max_texture_side: Option<usize>,
pub time: Option<f64>,
pub predicted_dt: f32,
pub modifiers: Modifiers,
pub events: Vec<Event>,
pub hovered_files: Vec<HoveredFile>,
pub dropped_files: Vec<DroppedFile>,
pub focused: bool,
}
impl Default for RawInput {
fn default() -> Self {
Self {
viewport_id: ViewportId::ROOT,
viewports: std::iter::once((ViewportId::ROOT, Default::default())).collect(),
screen_rect: None,
max_texture_side: None,
time: None,
predicted_dt: 1.0 / 60.0,
modifiers: Modifiers::default(),
events: vec![],
hovered_files: Default::default(),
dropped_files: Default::default(),
focused: true, }
}
}
impl RawInput {
#[inline]
pub fn viewport(&self) -> &ViewportInfo {
self.viewports.get(&self.viewport_id).expect("Failed to find current viewport in egui RawInput. This is the fault of the egui backend")
}
pub fn take(&mut self) -> RawInput {
RawInput {
viewport_id: self.viewport_id,
viewports: self.viewports.clone(),
screen_rect: self.screen_rect.take(),
max_texture_side: self.max_texture_side.take(),
time: self.time.take(),
predicted_dt: self.predicted_dt,
modifiers: self.modifiers,
events: std::mem::take(&mut self.events),
hovered_files: self.hovered_files.clone(),
dropped_files: std::mem::take(&mut self.dropped_files),
focused: self.focused,
}
}
pub fn append(&mut self, newer: Self) {
let Self {
viewport_id: viewport_ids,
viewports,
screen_rect,
max_texture_side,
time,
predicted_dt,
modifiers,
mut events,
mut hovered_files,
mut dropped_files,
focused,
} = newer;
self.viewport_id = viewport_ids;
self.viewports = viewports;
self.screen_rect = screen_rect.or(self.screen_rect);
self.max_texture_side = max_texture_side.or(self.max_texture_side);
self.time = time; self.predicted_dt = predicted_dt; self.modifiers = modifiers; self.events.append(&mut events);
self.hovered_files.append(&mut hovered_files);
self.dropped_files.append(&mut dropped_files);
self.focused = focused;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum ViewportEvent {
Close,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct ViewportInfo {
pub parent: Option<crate::ViewportId>,
pub title: Option<String>,
pub events: Vec<ViewportEvent>,
pub native_pixels_per_point: Option<f32>,
pub monitor_size: Option<Vec2>,
pub inner_rect: Option<Rect>,
pub outer_rect: Option<Rect>,
pub minimized: Option<bool>,
pub maximized: Option<bool>,
pub fullscreen: Option<bool>,
pub focused: Option<bool>,
}
impl ViewportInfo {
pub fn close_requested(&self) -> bool {
self.events
.iter()
.any(|&event| event == ViewportEvent::Close)
}
pub fn ui(&self, ui: &mut crate::Ui) {
let Self {
parent,
title,
events,
native_pixels_per_point,
monitor_size,
inner_rect,
outer_rect,
minimized,
maximized,
fullscreen,
focused,
} = self;
crate::Grid::new("viewport_info").show(ui, |ui| {
ui.label("Parent:");
ui.label(opt_as_str(parent));
ui.end_row();
ui.label("Title:");
ui.label(opt_as_str(title));
ui.end_row();
ui.label("Events:");
ui.label(format!("{events:?}"));
ui.end_row();
ui.label("Native pixels-per-point:");
ui.label(opt_as_str(native_pixels_per_point));
ui.end_row();
ui.label("Monitor size:");
ui.label(opt_as_str(monitor_size));
ui.end_row();
ui.label("Inner rect:");
ui.label(opt_rect_as_string(inner_rect));
ui.end_row();
ui.label("Outer rect:");
ui.label(opt_rect_as_string(outer_rect));
ui.end_row();
ui.label("Minimized:");
ui.label(opt_as_str(minimized));
ui.end_row();
ui.label("Maximized:");
ui.label(opt_as_str(maximized));
ui.end_row();
ui.label("Fullscreen:");
ui.label(opt_as_str(fullscreen));
ui.end_row();
ui.label("Focused:");
ui.label(opt_as_str(focused));
ui.end_row();
fn opt_rect_as_string(v: &Option<Rect>) -> String {
v.as_ref().map_or(String::new(), |r| {
format!("Pos: {:?}, size: {:?}", r.min, r.size())
})
}
fn opt_as_str<T: std::fmt::Debug>(v: &Option<T>) -> String {
v.as_ref().map_or(String::new(), |v| format!("{v:?}"))
}
});
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct HoveredFile {
pub path: Option<std::path::PathBuf>,
pub mime: String,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct DroppedFile {
pub path: Option<std::path::PathBuf>,
pub name: String,
pub mime: String,
pub last_modified: Option<std::time::SystemTime>,
pub bytes: Option<std::sync::Arc<[u8]>>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum Event {
Copy,
Cut,
Paste(String),
Text(String),
Key {
key: Key,
pressed: bool,
repeat: bool,
modifiers: Modifiers,
},
PointerMoved(Pos2),
PointerButton {
pos: Pos2,
button: PointerButton,
pressed: bool,
modifiers: Modifiers,
},
PointerGone,
Scroll(Vec2),
Zoom(f32),
CompositionStart,
CompositionUpdate(String),
CompositionEnd(String),
Touch {
device_id: TouchDeviceId,
id: TouchId,
phase: TouchPhase,
pos: Pos2,
force: Option<f32>,
},
MouseWheel {
unit: MouseWheelUnit,
delta: Vec2,
modifiers: Modifiers,
},
WindowFocused(bool),
#[cfg(feature = "accesskit")]
AccessKitActionRequest(accesskit::ActionRequest),
Screenshot {
viewport_id: crate::ViewportId,
image: std::sync::Arc<ColorImage>,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum PointerButton {
Primary = 0,
Secondary = 1,
Middle = 2,
Extra1 = 3,
Extra2 = 4,
}
pub const NUM_POINTER_BUTTONS: usize = 5;
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Modifiers {
pub alt: bool,
pub ctrl: bool,
pub shift: bool,
pub mac_cmd: bool,
pub command: bool,
}
impl Modifiers {
pub const NONE: Self = Self {
alt: false,
ctrl: false,
shift: false,
mac_cmd: false,
command: false,
};
pub const ALT: Self = Self {
alt: true,
ctrl: false,
shift: false,
mac_cmd: false,
command: false,
};
pub const CTRL: Self = Self {
alt: false,
ctrl: true,
shift: false,
mac_cmd: false,
command: false,
};
pub const SHIFT: Self = Self {
alt: false,
ctrl: false,
shift: true,
mac_cmd: false,
command: false,
};
#[deprecated = "Use `Modifiers::ALT | Modifiers::SHIFT` instead"]
pub const ALT_SHIFT: Self = Self {
alt: true,
ctrl: false,
shift: true,
mac_cmd: false,
command: false,
};
pub const MAC_CMD: Self = Self {
alt: false,
ctrl: false,
shift: false,
mac_cmd: true,
command: false,
};
pub const COMMAND: Self = Self {
alt: false,
ctrl: false,
shift: false,
mac_cmd: false,
command: true,
};
#[inline]
pub const fn plus(self, rhs: Self) -> Self {
Self {
alt: self.alt | rhs.alt,
ctrl: self.ctrl | rhs.ctrl,
shift: self.shift | rhs.shift,
mac_cmd: self.mac_cmd | rhs.mac_cmd,
command: self.command | rhs.command,
}
}
#[inline]
pub fn is_none(&self) -> bool {
self == &Self::default()
}
#[inline]
pub fn any(&self) -> bool {
!self.is_none()
}
#[inline]
pub fn all(&self) -> bool {
self.alt && self.ctrl && self.shift && self.command
}
#[inline]
pub fn shift_only(&self) -> bool {
self.shift && !(self.alt || self.command)
}
#[inline]
pub fn command_only(&self) -> bool {
!self.alt && !self.shift && self.command
}
pub fn matches(&self, pattern: Modifiers) -> bool {
if pattern.alt != self.alt || pattern.shift != self.shift {
return false;
}
if pattern.mac_cmd {
if !self.mac_cmd {
return false;
}
if pattern.ctrl != self.ctrl {
return false;
}
return true;
}
if !pattern.ctrl && !pattern.command {
return !self.ctrl && !self.command;
}
if pattern.ctrl && !self.ctrl {
return false;
}
if pattern.command && !self.command {
return false;
}
true
}
pub fn contains(&self, query: Modifiers) -> bool {
if query == Modifiers::default() {
return true;
}
let Modifiers {
alt,
ctrl,
shift,
mac_cmd,
command,
} = *self;
if alt && query.alt {
return self.contains(Modifiers {
alt: false,
..query
});
}
if shift && query.shift {
return self.contains(Modifiers {
shift: false,
..query
});
}
if (ctrl || command) && (query.ctrl || query.command) {
return self.contains(Modifiers {
command: false,
ctrl: false,
..query
});
}
if (mac_cmd || command) && (query.mac_cmd || query.command) {
return self.contains(Modifiers {
mac_cmd: false,
command: false,
..query
});
}
false
}
}
impl std::ops::BitOr for Modifiers {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
self.plus(rhs)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ModifierNames<'a> {
pub is_short: bool,
pub alt: &'a str,
pub ctrl: &'a str,
pub shift: &'a str,
pub mac_cmd: &'a str,
pub mac_alt: &'a str,
pub concat: &'a str,
}
impl ModifierNames<'static> {
pub const SYMBOLS: Self = Self {
is_short: true,
alt: "⌥",
ctrl: "⌃",
shift: "⇧",
mac_cmd: "⌘",
mac_alt: "⌥",
concat: "",
};
pub const NAMES: Self = Self {
is_short: false,
alt: "Alt",
ctrl: "Ctrl",
shift: "Shift",
mac_cmd: "Cmd",
mac_alt: "Option",
concat: "+",
};
}
impl<'a> ModifierNames<'a> {
pub fn format(&self, modifiers: &Modifiers, is_mac: bool) -> String {
let mut s = String::new();
let mut append_if = |modifier_is_active, modifier_name| {
if modifier_is_active {
if !s.is_empty() {
s += self.concat;
}
s += modifier_name;
}
};
if is_mac {
append_if(modifiers.ctrl, self.ctrl);
append_if(modifiers.shift, self.shift);
append_if(modifiers.alt, self.mac_alt);
append_if(modifiers.mac_cmd || modifiers.command, self.mac_cmd);
} else {
append_if(modifiers.ctrl || modifiers.command, self.ctrl);
append_if(modifiers.alt, self.alt);
append_if(modifiers.shift, self.shift);
}
s
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum Key {
ArrowDown,
ArrowLeft,
ArrowRight,
ArrowUp,
Escape,
Tab,
Backspace,
Enter,
Space,
Insert,
Delete,
Home,
End,
PageUp,
PageDown,
Minus,
PlusEquals,
Num0,
Num1,
Num2,
Num3,
Num4,
Num5,
Num6,
Num7,
Num8,
Num9,
A, B,
C, D, E, F, G, H, I, J, K, L,
M,
N,
O, P, Q,
R, S, T, U, V, W, X, Y,
Z,
F1,
F2,
F3,
F4,
F5, F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
}
impl Key {
pub fn symbol_or_name(self) -> &'static str {
match self {
Key::ArrowDown => "⏷",
Key::ArrowLeft => "⏴",
Key::ArrowRight => "⏵",
Key::ArrowUp => "⏶",
Key::Minus => crate::MINUS_CHAR_STR,
Key::PlusEquals => "+",
_ => self.name(),
}
}
pub fn name(self) -> &'static str {
match self {
Key::ArrowDown => "Down",
Key::ArrowLeft => "Left",
Key::ArrowRight => "Right",
Key::ArrowUp => "Up",
Key::Escape => "Escape",
Key::Tab => "Tab",
Key::Backspace => "Backspace",
Key::Enter => "Enter",
Key::Space => "Space",
Key::Insert => "Insert",
Key::Delete => "Delete",
Key::Home => "Home",
Key::End => "End",
Key::PageUp => "PageUp",
Key::PageDown => "PageDown",
Key::Minus => "Minus",
Key::PlusEquals => "Plus",
Key::Num0 => "0",
Key::Num1 => "1",
Key::Num2 => "2",
Key::Num3 => "3",
Key::Num4 => "4",
Key::Num5 => "5",
Key::Num6 => "6",
Key::Num7 => "7",
Key::Num8 => "8",
Key::Num9 => "9",
Key::A => "A",
Key::B => "B",
Key::C => "C",
Key::D => "D",
Key::E => "E",
Key::F => "F",
Key::G => "G",
Key::H => "H",
Key::I => "I",
Key::J => "J",
Key::K => "K",
Key::L => "L",
Key::M => "M",
Key::N => "N",
Key::O => "O",
Key::P => "P",
Key::Q => "Q",
Key::R => "R",
Key::S => "S",
Key::T => "T",
Key::U => "U",
Key::V => "V",
Key::W => "W",
Key::X => "X",
Key::Y => "Y",
Key::Z => "Z",
Key::F1 => "F1",
Key::F2 => "F2",
Key::F3 => "F3",
Key::F4 => "F4",
Key::F5 => "F5",
Key::F6 => "F6",
Key::F7 => "F7",
Key::F8 => "F8",
Key::F9 => "F9",
Key::F10 => "F10",
Key::F11 => "F11",
Key::F12 => "F12",
Key::F13 => "F13",
Key::F14 => "F14",
Key::F15 => "F15",
Key::F16 => "F16",
Key::F17 => "F17",
Key::F18 => "F18",
Key::F19 => "F19",
Key::F20 => "F20",
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct KeyboardShortcut {
pub modifiers: Modifiers,
pub key: Key,
}
impl KeyboardShortcut {
pub const fn new(modifiers: Modifiers, key: Key) -> Self {
Self { modifiers, key }
}
pub fn format(&self, names: &ModifierNames<'_>, is_mac: bool) -> String {
let mut s = names.format(&self.modifiers, is_mac);
if !s.is_empty() {
s += names.concat;
}
if names.is_short {
s += self.key.symbol_or_name();
} else {
s += self.key.name();
}
s
}
}
#[test]
fn format_kb_shortcut() {
let cmd_shift_f = KeyboardShortcut::new(Modifiers::COMMAND | Modifiers::SHIFT, Key::F);
assert_eq!(
cmd_shift_f.format(&ModifierNames::NAMES, false),
"Ctrl+Shift+F"
);
assert_eq!(
cmd_shift_f.format(&ModifierNames::NAMES, true),
"Shift+Cmd+F"
);
assert_eq!(cmd_shift_f.format(&ModifierNames::SYMBOLS, false), "⌃⇧F");
assert_eq!(cmd_shift_f.format(&ModifierNames::SYMBOLS, true), "⇧⌘F");
}
impl RawInput {
pub fn ui(&self, ui: &mut crate::Ui) {
let Self {
viewport_id,
viewports,
screen_rect,
max_texture_side,
time,
predicted_dt,
modifiers,
events,
hovered_files,
dropped_files,
focused,
} = self;
ui.label(format!("Active viwport: {viewport_id:?}"));
for (id, viewport) in viewports {
ui.group(|ui| {
ui.label(format!("Viewport {id:?}"));
ui.push_id(id, |ui| {
viewport.ui(ui);
});
});
}
ui.label(format!("screen_rect: {screen_rect:?} points"));
ui.label(format!("max_texture_side: {max_texture_side:?}"));
if let Some(time) = time {
ui.label(format!("time: {time:.3} s"));
} else {
ui.label("time: None");
}
ui.label(format!("predicted_dt: {:.1} ms", 1e3 * predicted_dt));
ui.label(format!("modifiers: {modifiers:#?}"));
ui.label(format!("hovered_files: {}", hovered_files.len()));
ui.label(format!("dropped_files: {}", dropped_files.len()));
ui.label(format!("focused: {focused}"));
ui.scope(|ui| {
ui.set_min_height(150.0);
ui.label(format!("events: {events:#?}"))
.on_hover_text("key presses etc");
});
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct TouchDeviceId(pub u64);
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct TouchId(pub u64);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum TouchPhase {
Start,
Move,
End,
Cancel,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum MouseWheelUnit {
Point,
Line,
Page,
}
impl From<u64> for TouchId {
fn from(id: u64) -> Self {
Self(id)
}
}
impl From<i32> for TouchId {
fn from(id: i32) -> Self {
Self(id as u64)
}
}
impl From<u32> for TouchId {
fn from(id: u32) -> Self {
Self(id as u64)
}
}
#[derive(Clone, Copy, Debug)]
pub struct EventFilter {
pub tab: bool,
pub arrows: bool,
pub escape: bool,
}
#[allow(clippy::derivable_impls)] impl Default for EventFilter {
fn default() -> Self {
Self {
tab: false,
arrows: false,
escape: false,
}
}
}
impl EventFilter {
pub fn matches(&self, event: &Event) -> bool {
if let Event::Key { key, .. } = event {
match key {
crate::Key::Tab => self.tab,
crate::Key::ArrowUp
| crate::Key::ArrowRight
| crate::Key::ArrowDown
| crate::Key::ArrowLeft => self.arrows,
crate::Key::Escape => self.escape,
_ => true,
}
} else {
true
}
}
}