use std::sync::Arc;
use epaint::{Pos2, Vec2};
use crate::{Context, Id};
#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum ViewportClass {
#[default]
Root,
Deferred,
Immediate,
Embedded,
}
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct ViewportId(pub Id);
impl Default for ViewportId {
#[inline]
fn default() -> Self {
Self::ROOT
}
}
impl std::fmt::Debug for ViewportId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.short_debug_format().fmt(f)
}
}
impl ViewportId {
pub const ROOT: Self = Self(Id::NULL);
#[inline]
pub fn from_hash_of(source: impl std::hash::Hash) -> Self {
Self(Id::new(source))
}
}
impl From<ViewportId> for Id {
#[inline]
fn from(id: ViewportId) -> Self {
id.0
}
}
impl nohash_hasher::IsEnabled for ViewportId {}
pub type ViewportIdSet = nohash_hasher::IntSet<ViewportId>;
pub type ViewportIdMap<T> = nohash_hasher::IntMap<ViewportId, T>;
#[derive(Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct IconData {
pub rgba: Vec<u8>,
pub width: u32,
pub height: u32,
}
impl IconData {
#[inline]
pub fn is_empty(&self) -> bool {
self.rgba.is_empty()
}
}
impl std::fmt::Debug for IconData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IconData")
.field("width", &self.width)
.field("height", &self.height)
.finish_non_exhaustive()
}
}
impl From<IconData> for epaint::ColorImage {
fn from(icon: IconData) -> Self {
profiling::function_scope!();
let IconData {
rgba,
width,
height,
} = icon;
Self::from_rgba_premultiplied([width as usize, height as usize], &rgba)
}
}
impl From<&IconData> for epaint::ColorImage {
fn from(icon: &IconData) -> Self {
profiling::function_scope!();
let IconData {
rgba,
width,
height,
} = icon;
Self::from_rgba_premultiplied([*width as usize, *height as usize], rgba)
}
}
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct ViewportIdPair {
pub this: ViewportId,
pub parent: ViewportId,
}
impl Default for ViewportIdPair {
#[inline]
fn default() -> Self {
Self::ROOT
}
}
impl ViewportIdPair {
pub const ROOT: Self = Self {
this: ViewportId::ROOT,
parent: ViewportId::ROOT,
};
#[inline]
pub fn from_self_and_parent(this: ViewportId, parent: ViewportId) -> Self {
Self { this, parent }
}
}
pub type DeferredViewportUiCallback = dyn Fn(&Context) + Sync + Send;
pub type ImmediateViewportRendererCallback = dyn for<'a> Fn(&Context, ImmediateViewport<'a>);
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct ViewportBuilder {
pub title: Option<String>,
pub app_id: Option<String>,
pub position: Option<Pos2>,
pub inner_size: Option<Vec2>,
pub min_inner_size: Option<Vec2>,
pub max_inner_size: Option<Vec2>,
pub clamp_size_to_monitor_size: Option<bool>,
pub fullscreen: Option<bool>,
pub maximized: Option<bool>,
pub resizable: Option<bool>,
pub transparent: Option<bool>,
pub decorations: Option<bool>,
pub icon: Option<Arc<IconData>>,
pub active: Option<bool>,
pub visible: Option<bool>,
pub fullsize_content_view: Option<bool>,
pub movable_by_window_background: Option<bool>,
pub title_shown: Option<bool>,
pub titlebar_buttons_shown: Option<bool>,
pub titlebar_shown: Option<bool>,
pub has_shadow: Option<bool>,
pub drag_and_drop: Option<bool>,
pub taskbar: Option<bool>,
pub close_button: Option<bool>,
pub minimize_button: Option<bool>,
pub maximize_button: Option<bool>,
pub window_level: Option<WindowLevel>,
pub mouse_passthrough: Option<bool>,
pub window_type: Option<X11WindowType>,
}
impl ViewportBuilder {
#[inline]
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
#[inline]
pub fn with_decorations(mut self, decorations: bool) -> Self {
self.decorations = Some(decorations);
self
}
#[inline]
pub fn with_fullscreen(mut self, fullscreen: bool) -> Self {
self.fullscreen = Some(fullscreen);
self
}
#[inline]
pub fn with_maximized(mut self, maximized: bool) -> Self {
self.maximized = Some(maximized);
self
}
#[inline]
pub fn with_resizable(mut self, resizable: bool) -> Self {
self.resizable = Some(resizable);
self
}
#[inline]
pub fn with_transparent(mut self, transparent: bool) -> Self {
self.transparent = Some(transparent);
self
}
#[inline]
pub fn with_icon(mut self, icon: impl Into<Arc<IconData>>) -> Self {
self.icon = Some(icon.into());
self
}
#[inline]
pub fn with_active(mut self, active: bool) -> Self {
self.active = Some(active);
self
}
#[inline]
pub fn with_visible(mut self, visible: bool) -> Self {
self.visible = Some(visible);
self
}
#[inline]
pub fn with_fullsize_content_view(mut self, value: bool) -> Self {
self.fullsize_content_view = Some(value);
self
}
#[inline]
pub fn with_movable_by_background(mut self, value: bool) -> Self {
self.movable_by_window_background = Some(value);
self
}
#[inline]
pub fn with_title_shown(mut self, title_shown: bool) -> Self {
self.title_shown = Some(title_shown);
self
}
#[inline]
pub fn with_titlebar_buttons_shown(mut self, titlebar_buttons_shown: bool) -> Self {
self.titlebar_buttons_shown = Some(titlebar_buttons_shown);
self
}
#[inline]
pub fn with_titlebar_shown(mut self, shown: bool) -> Self {
self.titlebar_shown = Some(shown);
self
}
#[inline]
pub fn with_has_shadow(mut self, has_shadow: bool) -> Self {
self.has_shadow = Some(has_shadow);
self
}
#[inline]
pub fn with_taskbar(mut self, show: bool) -> Self {
self.taskbar = Some(show);
self
}
#[inline]
pub fn with_inner_size(mut self, size: impl Into<Vec2>) -> Self {
self.inner_size = Some(size.into());
self
}
#[inline]
pub fn with_min_inner_size(mut self, size: impl Into<Vec2>) -> Self {
self.min_inner_size = Some(size.into());
self
}
#[inline]
pub fn with_max_inner_size(mut self, size: impl Into<Vec2>) -> Self {
self.max_inner_size = Some(size.into());
self
}
#[inline]
pub fn with_clamp_size_to_monitor_size(mut self, value: bool) -> Self {
self.clamp_size_to_monitor_size = Some(value);
self
}
#[inline]
pub fn with_close_button(mut self, value: bool) -> Self {
self.close_button = Some(value);
self
}
#[inline]
pub fn with_minimize_button(mut self, value: bool) -> Self {
self.minimize_button = Some(value);
self
}
#[inline]
pub fn with_maximize_button(mut self, value: bool) -> Self {
self.maximize_button = Some(value);
self
}
#[inline]
pub fn with_drag_and_drop(mut self, value: bool) -> Self {
self.drag_and_drop = Some(value);
self
}
#[inline]
pub fn with_position(mut self, pos: impl Into<Pos2>) -> Self {
self.position = Some(pos.into());
self
}
#[inline]
pub fn with_app_id(mut self, app_id: impl Into<String>) -> Self {
self.app_id = Some(app_id.into());
self
}
#[inline]
pub fn with_window_level(mut self, level: WindowLevel) -> Self {
self.window_level = Some(level);
self
}
#[inline]
pub fn with_always_on_top(self) -> Self {
self.with_window_level(WindowLevel::AlwaysOnTop)
}
#[inline]
pub fn with_mouse_passthrough(mut self, value: bool) -> Self {
self.mouse_passthrough = Some(value);
self
}
#[inline]
pub fn with_window_type(mut self, value: X11WindowType) -> Self {
self.window_type = Some(value);
self
}
#[must_use]
pub fn patch(&mut self, new_vp_builder: Self) -> (Vec<ViewportCommand>, bool) {
let Self {
title: new_title,
app_id: new_app_id,
position: new_position,
inner_size: new_inner_size,
min_inner_size: new_min_inner_size,
max_inner_size: new_max_inner_size,
clamp_size_to_monitor_size: new_clamp_size_to_monitor_size,
fullscreen: new_fullscreen,
maximized: new_maximized,
resizable: new_resizable,
transparent: new_transparent,
decorations: new_decorations,
icon: new_icon,
active: new_active,
visible: new_visible,
drag_and_drop: new_drag_and_drop,
fullsize_content_view: new_fullsize_content_view,
movable_by_window_background: new_movable_by_window_background,
title_shown: new_title_shown,
titlebar_buttons_shown: new_titlebar_buttons_shown,
titlebar_shown: new_titlebar_shown,
has_shadow: new_has_shadow,
close_button: new_close_button,
minimize_button: new_minimize_button,
maximize_button: new_maximize_button,
window_level: new_window_level,
mouse_passthrough: new_mouse_passthrough,
taskbar: new_taskbar,
window_type: new_window_type,
} = new_vp_builder;
let mut commands = Vec::new();
if let Some(new_title) = new_title {
if Some(&new_title) != self.title.as_ref() {
self.title = Some(new_title.clone());
commands.push(ViewportCommand::Title(new_title));
}
}
if let Some(new_position) = new_position {
if Some(new_position) != self.position {
self.position = Some(new_position);
commands.push(ViewportCommand::OuterPosition(new_position));
}
}
if let Some(new_inner_size) = new_inner_size {
if Some(new_inner_size) != self.inner_size {
self.inner_size = Some(new_inner_size);
commands.push(ViewportCommand::InnerSize(new_inner_size));
}
}
if let Some(new_min_inner_size) = new_min_inner_size {
if Some(new_min_inner_size) != self.min_inner_size {
self.min_inner_size = Some(new_min_inner_size);
commands.push(ViewportCommand::MinInnerSize(new_min_inner_size));
}
}
if let Some(new_max_inner_size) = new_max_inner_size {
if Some(new_max_inner_size) != self.max_inner_size {
self.max_inner_size = Some(new_max_inner_size);
commands.push(ViewportCommand::MaxInnerSize(new_max_inner_size));
}
}
if let Some(new_fullscreen) = new_fullscreen {
if Some(new_fullscreen) != self.fullscreen {
self.fullscreen = Some(new_fullscreen);
commands.push(ViewportCommand::Fullscreen(new_fullscreen));
}
}
if let Some(new_maximized) = new_maximized {
if Some(new_maximized) != self.maximized {
self.maximized = Some(new_maximized);
commands.push(ViewportCommand::Maximized(new_maximized));
}
}
if let Some(new_resizable) = new_resizable {
if Some(new_resizable) != self.resizable {
self.resizable = Some(new_resizable);
commands.push(ViewportCommand::Resizable(new_resizable));
}
}
if let Some(new_transparent) = new_transparent {
if Some(new_transparent) != self.transparent {
self.transparent = Some(new_transparent);
commands.push(ViewportCommand::Transparent(new_transparent));
}
}
if let Some(new_decorations) = new_decorations {
if Some(new_decorations) != self.decorations {
self.decorations = Some(new_decorations);
commands.push(ViewportCommand::Decorations(new_decorations));
}
}
if let Some(new_icon) = new_icon {
let is_new = match &self.icon {
Some(existing) => !Arc::ptr_eq(&new_icon, existing),
None => true,
};
if is_new {
commands.push(ViewportCommand::Icon(Some(new_icon.clone())));
self.icon = Some(new_icon);
}
}
if let Some(new_visible) = new_visible {
if Some(new_visible) != self.visible {
self.visible = Some(new_visible);
commands.push(ViewportCommand::Visible(new_visible));
}
}
if let Some(new_mouse_passthrough) = new_mouse_passthrough {
if Some(new_mouse_passthrough) != self.mouse_passthrough {
self.mouse_passthrough = Some(new_mouse_passthrough);
commands.push(ViewportCommand::MousePassthrough(new_mouse_passthrough));
}
}
if let Some(new_window_level) = new_window_level {
if Some(new_window_level) != self.window_level {
self.window_level = Some(new_window_level);
commands.push(ViewportCommand::WindowLevel(new_window_level));
}
}
let mut recreate_window = false;
if new_clamp_size_to_monitor_size.is_some()
&& self.clamp_size_to_monitor_size != new_clamp_size_to_monitor_size
{
self.clamp_size_to_monitor_size = new_clamp_size_to_monitor_size;
recreate_window = true;
}
if new_active.is_some() && self.active != new_active {
self.active = new_active;
recreate_window = true;
}
if new_app_id.is_some() && self.app_id != new_app_id {
self.app_id = new_app_id;
recreate_window = true;
}
if new_close_button.is_some() && self.close_button != new_close_button {
self.close_button = new_close_button;
recreate_window = true;
}
if new_minimize_button.is_some() && self.minimize_button != new_minimize_button {
self.minimize_button = new_minimize_button;
recreate_window = true;
}
if new_maximize_button.is_some() && self.maximize_button != new_maximize_button {
self.maximize_button = new_maximize_button;
recreate_window = true;
}
if new_title_shown.is_some() && self.title_shown != new_title_shown {
self.title_shown = new_title_shown;
recreate_window = true;
}
if new_titlebar_buttons_shown.is_some()
&& self.titlebar_buttons_shown != new_titlebar_buttons_shown
{
self.titlebar_buttons_shown = new_titlebar_buttons_shown;
recreate_window = true;
}
if new_titlebar_shown.is_some() && self.titlebar_shown != new_titlebar_shown {
self.titlebar_shown = new_titlebar_shown;
recreate_window = true;
}
if new_has_shadow.is_some() && self.has_shadow != new_has_shadow {
self.has_shadow = new_has_shadow;
recreate_window = true;
}
if new_taskbar.is_some() && self.taskbar != new_taskbar {
self.taskbar = new_taskbar;
recreate_window = true;
}
if new_fullsize_content_view.is_some()
&& self.fullsize_content_view != new_fullsize_content_view
{
self.fullsize_content_view = new_fullsize_content_view;
recreate_window = true;
}
if new_movable_by_window_background.is_some()
&& self.movable_by_window_background != new_movable_by_window_background
{
self.movable_by_window_background = new_movable_by_window_background;
recreate_window = true;
}
if new_drag_and_drop.is_some() && self.drag_and_drop != new_drag_and_drop {
self.drag_and_drop = new_drag_and_drop;
recreate_window = true;
}
if new_window_type.is_some() && self.window_type != new_window_type {
self.window_type = new_window_type;
recreate_window = true;
}
(commands, recreate_window)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum WindowLevel {
#[default]
Normal,
AlwaysOnBottom,
AlwaysOnTop,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum X11WindowType {
#[default]
Normal,
Desktop,
Dock,
Toolbar,
Menu,
Utility,
Splash,
Dialog,
DropdownMenu,
PopupMenu,
Tooltip,
Notification,
Combo,
Dnd,
}
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum IMEPurpose {
#[default]
Normal,
Password,
Terminal,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum SystemTheme {
#[default]
SystemDefault,
Light,
Dark,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum CursorGrab {
#[default]
None,
Confined,
Locked,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum ResizeDirection {
North,
South,
East,
West,
NorthEast,
SouthEast,
NorthWest,
SouthWest,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum ViewportCommand {
Close,
CancelClose,
Title(String),
Transparent(bool),
Visible(bool),
StartDrag,
OuterPosition(Pos2),
InnerSize(Vec2),
MinInnerSize(Vec2),
MaxInnerSize(Vec2),
ResizeIncrements(Option<Vec2>),
BeginResize(ResizeDirection),
Resizable(bool),
EnableButtons {
close: bool,
minimized: bool,
maximize: bool,
},
Minimized(bool),
Maximized(bool),
Fullscreen(bool),
Decorations(bool),
WindowLevel(WindowLevel),
Icon(Option<Arc<IconData>>),
IMERect(crate::Rect),
IMEAllowed(bool),
IMEPurpose(IMEPurpose),
Focus,
RequestUserAttention(crate::UserAttentionType),
SetTheme(SystemTheme),
ContentProtected(bool),
CursorPosition(Pos2),
CursorGrab(CursorGrab),
CursorVisible(bool),
MousePassthrough(bool),
Screenshot(crate::UserData),
RequestCut,
RequestCopy,
RequestPaste,
}
impl ViewportCommand {
pub fn center_on_screen(ctx: &crate::Context) -> Option<Self> {
ctx.input(|i| {
let outer_rect = i.viewport().outer_rect?;
let size = outer_rect.size();
let monitor_size = i.viewport().monitor_size?;
if 1.0 < monitor_size.x && 1.0 < monitor_size.y {
let x = (monitor_size.x - size.x) / 2.0;
let y = (monitor_size.y - size.y) / 2.0;
Some(Self::OuterPosition([x, y].into()))
} else {
None
}
})
}
pub fn requires_parent_repaint(&self) -> bool {
self == &Self::Close
}
}
#[derive(Clone)]
pub struct ViewportOutput {
pub parent: ViewportId,
pub class: ViewportClass,
pub builder: ViewportBuilder,
pub viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,
pub commands: Vec<ViewportCommand>,
pub repaint_delay: std::time::Duration,
}
impl ViewportOutput {
pub fn append(&mut self, newer: Self) {
let Self {
parent,
class,
builder,
viewport_ui_cb,
mut commands,
repaint_delay,
} = newer;
self.parent = parent;
self.class = class;
let _ = self.builder.patch(builder); self.viewport_ui_cb = viewport_ui_cb;
self.commands.append(&mut commands);
self.repaint_delay = self.repaint_delay.min(repaint_delay);
}
}
pub struct ImmediateViewport<'a> {
pub ids: ViewportIdPair,
pub builder: ViewportBuilder,
pub viewport_ui_cb: Box<dyn FnMut(&Context) + 'a>,
}