use std::fmt;
use crate::{
dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Pixel, PixelUnit, Position, Size},
error::{ExternalError, NotSupportedError, OsError},
event_loop::EventLoopWindowTarget,
monitor::{MonitorHandle, VideoMode},
platform_impl,
};
pub use crate::icon::{BadIcon, Icon};
#[derive(Debug, Clone, Copy)]
pub enum ProgressState {
None,
Normal,
Indeterminate,
Paused,
Error,
}
pub struct ProgressBarState {
pub state: Option<ProgressState>,
pub progress: Option<u64>,
pub desktop_filename: Option<String>,
}
pub struct Window {
pub(crate) window: platform_impl::Window,
}
impl fmt::Debug for Window {
fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
fmtr.pad("Window { .. }")
}
}
impl Drop for Window {
fn drop(&mut self) {
if let Some(Fullscreen::Exclusive(_)) = self.fullscreen() {
self.set_fullscreen(None);
}
}
}
pub type RGBA = (u8, u8, u8, u8);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(pub(crate) platform_impl::WindowId);
impl WindowId {
pub unsafe fn dummy() -> Self {
WindowId(platform_impl::WindowId::dummy())
}
}
#[derive(Clone, Default)]
pub struct WindowBuilder {
pub window: WindowAttributes,
pub(crate) platform_specific: platform_impl::PlatformSpecificWindowBuilderAttributes,
}
impl fmt::Debug for WindowBuilder {
fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
fmtr
.debug_struct("WindowBuilder")
.field("window", &self.window)
.finish()
}
}
#[derive(Debug, Clone)]
pub struct WindowAttributes {
pub inner_size: Option<Size>,
pub inner_size_constraints: WindowSizeConstraints,
pub position: Option<Position>,
pub resizable: bool,
pub minimizable: bool,
pub maximizable: bool,
pub closable: bool,
pub fullscreen: Option<Fullscreen>,
pub title: String,
pub maximized: bool,
pub visible: bool,
pub transparent: bool,
pub decorations: bool,
pub always_on_top: bool,
pub always_on_bottom: bool,
pub window_icon: Option<Icon>,
pub preferred_theme: Option<Theme>,
pub focused: bool,
pub focusable: bool,
pub content_protection: bool,
pub visible_on_all_workspaces: bool,
pub background_color: Option<RGBA>,
}
impl Default for WindowAttributes {
#[inline]
fn default() -> WindowAttributes {
WindowAttributes {
inner_size: None,
inner_size_constraints: Default::default(),
position: None,
resizable: true,
minimizable: true,
maximizable: true,
closable: true,
title: "tao window".to_owned(),
maximized: false,
fullscreen: None,
visible: true,
transparent: false,
decorations: true,
always_on_top: false,
always_on_bottom: false,
window_icon: None,
preferred_theme: None,
focused: true,
focusable: true,
content_protection: false,
visible_on_all_workspaces: false,
background_color: None,
}
}
}
impl WindowBuilder {
#[inline]
pub fn new() -> Self {
Default::default()
}
#[inline]
pub fn with_inner_size<S: Into<Size>>(mut self, size: S) -> Self {
self.window.inner_size = Some(size.into());
self
}
#[inline]
pub fn with_min_inner_size<S: Into<Size>>(mut self, min_size: S) -> Self {
let size: Size = min_size.into();
let (width, height) = crate::extract_width_height(size);
self.window.inner_size_constraints.min_width = Some(width);
self.window.inner_size_constraints.min_height = Some(height);
self
}
#[inline]
pub fn with_max_inner_size<S: Into<Size>>(mut self, max_size: S) -> Self {
let size: Size = max_size.into();
let (width, height) = crate::extract_width_height(size);
self.window.inner_size_constraints.max_width = Some(width);
self.window.inner_size_constraints.max_height = Some(height);
self
}
#[inline]
pub fn with_inner_size_constraints(mut self, constraints: WindowSizeConstraints) -> Self {
self.window.inner_size_constraints = constraints;
self
}
#[inline]
pub fn with_position<P: Into<Position>>(mut self, position: P) -> Self {
self.window.position = Some(position.into());
self
}
#[inline]
pub fn with_resizable(mut self, resizable: bool) -> Self {
self.window.resizable = resizable;
self
}
#[inline]
pub fn with_minimizable(mut self, minimizable: bool) -> Self {
self.window.minimizable = minimizable;
self
}
#[inline]
pub fn with_maximizable(mut self, maximizable: bool) -> Self {
self.window.maximizable = maximizable;
self
}
#[inline]
pub fn with_closable(mut self, closable: bool) -> Self {
self.window.closable = closable;
self
}
#[inline]
pub fn with_title<T: Into<String>>(mut self, title: T) -> Self {
self.window.title = title.into();
self
}
#[inline]
pub fn with_fullscreen(mut self, fullscreen: Option<Fullscreen>) -> Self {
self.window.fullscreen = fullscreen;
self
}
#[inline]
pub fn with_maximized(mut self, maximized: bool) -> Self {
self.window.maximized = maximized;
self
}
#[inline]
pub fn with_visible(mut self, visible: bool) -> Self {
self.window.visible = visible;
self
}
#[inline]
pub fn with_transparent(mut self, transparent: bool) -> Self {
self.window.transparent = transparent;
self
}
#[inline]
pub fn with_decorations(mut self, decorations: bool) -> Self {
self.window.decorations = decorations;
self
}
#[inline]
pub fn with_always_on_bottom(mut self, always_on_bottom: bool) -> Self {
self.window.always_on_top = false;
self.window.always_on_bottom = always_on_bottom;
self
}
#[inline]
pub fn with_always_on_top(mut self, always_on_top: bool) -> Self {
self.window.always_on_bottom = false;
self.window.always_on_top = always_on_top;
self
}
#[inline]
pub fn with_window_icon(mut self, window_icon: Option<Icon>) -> Self {
self.window.window_icon = window_icon;
self
}
#[allow(rustdoc::broken_intra_doc_links)]
#[inline]
pub fn with_theme(mut self, theme: Option<Theme>) -> WindowBuilder {
self.window.preferred_theme = theme;
self
}
#[inline]
pub fn with_focused(mut self, focused: bool) -> WindowBuilder {
self.window.focused = focused;
self
}
#[inline]
pub fn with_focusable(mut self, focusable: bool) -> WindowBuilder {
self.window.focusable = focusable;
self
}
#[inline]
pub fn with_content_protection(mut self, protected: bool) -> WindowBuilder {
self.window.content_protection = protected;
self
}
#[inline]
pub fn with_visible_on_all_workspaces(mut self, visible: bool) -> WindowBuilder {
self.window.visible_on_all_workspaces = visible;
self
}
#[inline]
pub fn with_background_color(mut self, color: RGBA) -> WindowBuilder {
self.window.background_color = Some(color);
self
}
#[inline]
pub fn build<T: 'static>(
self,
window_target: &EventLoopWindowTarget<T>,
) -> Result<Window, OsError> {
platform_impl::Window::new(&window_target.p, self.window, self.platform_specific).map(
|window| {
window.request_redraw();
Window { window }
},
)
}
}
impl Window {
#[inline]
pub fn new<T: 'static>(event_loop: &EventLoopWindowTarget<T>) -> Result<Window, OsError> {
let builder = WindowBuilder::new();
builder.build(event_loop)
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId(self.window.id())
}
#[inline]
pub fn scale_factor(&self) -> f64 {
self.window.scale_factor()
}
#[inline]
pub fn request_redraw(&self) {
self.window.request_redraw()
}
}
impl Window {
#[inline]
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
self.window.inner_position()
}
#[inline]
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
self.window.outer_position()
}
#[inline]
pub fn set_outer_position<P: Into<Position>>(&self, position: P) {
self.window.set_outer_position(position.into())
}
#[inline]
pub fn inner_size(&self) -> PhysicalSize<u32> {
self.window.inner_size()
}
#[inline]
pub fn set_inner_size<S: Into<Size>>(&self, size: S) {
self.window.set_inner_size(size.into())
}
#[inline]
pub fn outer_size(&self) -> PhysicalSize<u32> {
self.window.outer_size()
}
#[inline]
pub fn set_min_inner_size<S: Into<Size>>(&self, min_size: Option<S>) {
self.window.set_min_inner_size(min_size.map(|s| s.into()))
}
#[inline]
pub fn set_max_inner_size<S: Into<Size>>(&self, max_size: Option<S>) {
self.window.set_max_inner_size(max_size.map(|s| s.into()))
}
#[inline]
pub fn set_inner_size_constraints(&self, constraints: WindowSizeConstraints) {
self.window.set_inner_size_constraints(constraints)
}
}
impl Window {
#[inline]
pub fn set_title(&self, title: &str) {
self.window.set_title(title)
}
#[inline]
pub fn title(&self) -> String {
self.window.title()
}
#[inline]
pub fn set_visible(&self, visible: bool) {
self.window.set_visible(visible)
}
#[inline]
pub fn set_focus(&self) {
self.window.set_focus()
}
#[inline]
pub fn set_focusable(&self, focusable: bool) {
self.window.set_focusable(focusable)
}
#[inline]
pub fn is_focused(&self) -> bool {
self.window.is_focused()
}
#[inline]
pub fn is_always_on_top(&self) -> bool {
self.window.is_always_on_top()
}
#[inline]
pub fn set_resizable(&self, resizable: bool) {
self.window.set_resizable(resizable)
}
#[inline]
pub fn set_minimizable(&self, minimizable: bool) {
self.window.set_minimizable(minimizable)
}
#[inline]
pub fn set_maximizable(&self, maximizable: bool) {
self.window.set_maximizable(maximizable)
}
#[inline]
pub fn set_closable(&self, closable: bool) {
self.window.set_closable(closable)
}
#[inline]
pub fn set_minimized(&self, minimized: bool) {
self.window.set_minimized(minimized);
}
#[inline]
pub fn set_maximized(&self, maximized: bool) {
self.window.set_maximized(maximized)
}
#[inline]
pub fn is_maximized(&self) -> bool {
self.window.is_maximized()
}
#[inline]
pub fn is_minimized(&self) -> bool {
self.window.is_minimized()
}
#[inline]
pub fn is_visible(&self) -> bool {
self.window.is_visible()
}
#[inline]
pub fn is_resizable(&self) -> bool {
self.window.is_resizable()
}
#[inline]
pub fn is_minimizable(&self) -> bool {
self.window.is_minimizable()
}
#[inline]
pub fn is_maximizable(&self) -> bool {
self.window.is_maximizable()
}
#[inline]
pub fn is_closable(&self) -> bool {
self.window.is_closable()
}
pub fn is_decorated(&self) -> bool {
self.window.is_decorated()
}
#[inline]
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
self.window.set_fullscreen(fullscreen)
}
#[inline]
pub fn fullscreen(&self) -> Option<Fullscreen> {
self.window.fullscreen()
}
#[inline]
pub fn set_decorations(&self, decorations: bool) {
self.window.set_decorations(decorations)
}
#[inline]
pub fn set_always_on_bottom(&self, always_on_bottom: bool) {
self.window.set_always_on_bottom(always_on_bottom)
}
#[inline]
pub fn set_always_on_top(&self, always_on_top: bool) {
self.window.set_always_on_top(always_on_top)
}
#[inline]
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
self.window.set_window_icon(window_icon)
}
#[inline]
pub fn set_ime_position<P: Into<Position>>(&self, position: P) {
self.window.set_ime_position(position.into())
}
#[inline]
pub fn set_progress_bar(&self, _progress: ProgressBarState) {
#[cfg(any(
windows,
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "macos",
))]
self.window.set_progress_bar(_progress)
}
#[inline]
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
self.window.request_user_attention(request_type)
}
#[inline]
pub fn theme(&self) -> Theme {
self.window.theme()
}
#[inline]
pub fn set_theme(&self, #[allow(unused)] theme: Option<Theme>) {
#[cfg(any(
windows,
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "macos",
))]
self.window.set_theme(theme)
}
pub fn set_content_protection(&self, #[allow(unused)] enabled: bool) {
#[cfg(any(target_os = "macos", target_os = "windows"))]
self.window.set_content_protection(enabled);
}
pub fn set_visible_on_all_workspaces(&self, #[allow(unused)] visible: bool) {
#[cfg(any(target_os = "macos", target_os = "linux"))]
self.window.set_visible_on_all_workspaces(visible)
}
#[inline]
pub fn set_background_color(&self, color: Option<RGBA>) {
self.window.set_background_color(color)
}
}
impl Window {
#[inline]
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
self.window.set_cursor_icon(cursor);
}
#[inline]
pub fn set_cursor_position<P: Into<Position>>(&self, position: P) -> Result<(), ExternalError> {
self.window.set_cursor_position(position.into())
}
#[inline]
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
self.window.set_cursor_grab(grab)
}
#[inline]
pub fn set_cursor_visible(&self, visible: bool) {
self.window.set_cursor_visible(visible)
}
#[inline]
pub fn drag_window(&self) -> Result<(), ExternalError> {
self.window.drag_window()
}
#[inline]
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
self.window.drag_resize_window(direction)
}
#[inline]
pub fn set_ignore_cursor_events(&self, ignore: bool) -> Result<(), ExternalError> {
self.window.set_ignore_cursor_events(ignore)
}
#[inline]
pub fn cursor_position(&self) -> Result<PhysicalPosition<f64>, ExternalError> {
self.window.cursor_position()
}
}
impl Window {
#[inline]
pub fn current_monitor(&self) -> Option<MonitorHandle> {
self.window.current_monitor()
}
#[inline]
pub fn monitor_from_point(&self, x: f64, y: f64) -> Option<MonitorHandle> {
self.window.monitor_from_point(x, y)
}
#[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
self
.window
.available_monitors()
.into_iter()
.map(|inner| MonitorHandle { inner })
}
#[inline]
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
self.window.primary_monitor()
}
}
#[cfg(feature = "rwh_04")]
unsafe impl rwh_04::HasRawWindowHandle for Window {
fn raw_window_handle(&self) -> rwh_04::RawWindowHandle {
self.window.raw_window_handle_rwh_04()
}
}
#[cfg(feature = "rwh_05")]
unsafe impl rwh_05::HasRawWindowHandle for Window {
fn raw_window_handle(&self) -> rwh_05::RawWindowHandle {
self.window.raw_window_handle_rwh_05()
}
}
#[cfg(feature = "rwh_05")]
unsafe impl rwh_05::HasRawDisplayHandle for Window {
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
self.window.raw_display_handle_rwh_05()
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for Window {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let raw = self.window.raw_window_handle_rwh_06()?;
Ok(unsafe { rwh_06::WindowHandle::borrow_raw(raw) })
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasDisplayHandle for Window {
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
let raw = self.window.raw_display_handle_rwh_06()?;
Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw) })
}
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Default)]
pub enum CursorIcon {
#[default]
Default,
Crosshair,
Hand,
Arrow,
Move,
Text,
Wait,
Help,
Progress,
NotAllowed,
ContextMenu,
Cell,
VerticalText,
Alias,
Copy,
NoDrop,
Grab,
Grabbing,
AllScroll,
ZoomIn,
ZoomOut,
EResize,
NResize,
NeResize,
NwResize,
SResize,
SeResize,
SwResize,
WResize,
EwResize,
NsResize,
NeswResize,
NwseResize,
ColResize,
RowResize,
}
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq)]
pub enum Fullscreen {
Exclusive(VideoMode),
Borderless(Option<MonitorHandle>),
}
#[non_exhaustive]
#[derive(Default, Clone, Copy, Debug, PartialEq)]
pub enum Theme {
#[default]
Light,
Dark,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum UserAttentionType {
Critical,
#[default]
Informational,
}
#[derive(Clone, Copy, PartialEq, Debug, Default)]
pub struct WindowSizeConstraints {
pub min_width: Option<PixelUnit>,
pub min_height: Option<PixelUnit>,
pub max_width: Option<PixelUnit>,
pub max_height: Option<PixelUnit>,
}
impl WindowSizeConstraints {
pub fn new(
min_width: Option<PixelUnit>,
min_height: Option<PixelUnit>,
max_width: Option<PixelUnit>,
max_height: Option<PixelUnit>,
) -> Self {
Self {
min_width,
min_height,
max_width,
max_height,
}
}
pub fn has_min(&self) -> bool {
self.min_width.is_some() || self.min_height.is_some()
}
pub fn has_max(&self) -> bool {
self.max_width.is_some() || self.max_height.is_some()
}
pub fn min_size_physical<T: Pixel>(&self, scale_factor: f64) -> PhysicalSize<T> {
PhysicalSize::new(
self
.min_width
.unwrap_or(PixelUnit::MIN)
.to_physical(scale_factor)
.0,
self
.min_height
.unwrap_or(PixelUnit::MIN)
.to_physical(scale_factor)
.0,
)
}
pub fn min_size_logical<T: Pixel>(&self, scale_factor: f64) -> LogicalSize<T> {
LogicalSize::new(
self
.min_width
.unwrap_or(PixelUnit::MIN)
.to_logical(scale_factor)
.0,
self
.min_height
.unwrap_or(PixelUnit::MIN)
.to_logical(scale_factor)
.0,
)
}
pub fn max_size_physical<T: Pixel>(&self, scale_factor: f64) -> PhysicalSize<T> {
PhysicalSize::new(
self
.max_width
.unwrap_or(PixelUnit::MAX)
.to_physical(scale_factor)
.0,
self
.max_height
.unwrap_or(PixelUnit::MAX)
.to_physical(scale_factor)
.0,
)
}
pub fn max_size_logical<T: Pixel>(&self, scale_factor: f64) -> LogicalSize<T> {
LogicalSize::new(
self
.max_width
.unwrap_or(PixelUnit::MAX)
.to_logical(scale_factor)
.0,
self
.max_height
.unwrap_or(PixelUnit::MAX)
.to_logical(scale_factor)
.0,
)
}
pub fn clamp(&self, desired_size: Size, scale_factor: f64) -> Size {
let min_size: PhysicalSize<f64> = self.min_size_physical(scale_factor);
let max_size: PhysicalSize<f64> = self.max_size_physical(scale_factor);
Size::clamp(desired_size, min_size.into(), max_size.into(), scale_factor)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ResizeDirection {
East,
North,
NorthEast,
NorthWest,
South,
SouthEast,
SouthWest,
West,
}
pub(crate) fn hit_test(
(left, top, right, bottom): (i32, i32, i32, i32),
cx: i32,
cy: i32,
border_x: i32,
border_y: i32,
) -> Option<ResizeDirection> {
const LEFT: isize = 0b0001;
const RIGHT: isize = 0b0010;
const TOP: isize = 0b0100;
const BOTTOM: isize = 0b1000;
const TOPLEFT: isize = TOP | LEFT;
const TOPRIGHT: isize = TOP | RIGHT;
const BOTTOMLEFT: isize = BOTTOM | LEFT;
const BOTTOMRIGHT: isize = BOTTOM | RIGHT;
#[rustfmt::skip]
let result = (LEFT * (cx < left + border_x) as isize)
| (RIGHT * (cx >= right - border_x) as isize)
| (TOP * (cy < top + border_y) as isize)
| (BOTTOM * (cy >= bottom - border_y) as isize);
match result {
LEFT => Some(ResizeDirection::West),
RIGHT => Some(ResizeDirection::East),
TOP => Some(ResizeDirection::North),
BOTTOM => Some(ResizeDirection::South),
TOPLEFT => Some(ResizeDirection::NorthWest),
TOPRIGHT => Some(ResizeDirection::NorthEast),
BOTTOMLEFT => Some(ResizeDirection::SouthWest),
BOTTOMRIGHT => Some(ResizeDirection::SouthEast),
_ => None,
}
}