mirror of
https://codeberg.org/waydeer/layer-shika.git
synced 2025-11-17 23:14:23 +00:00
refactor: unified dimensions system
This commit is contained in:
parent
c3c2690e84
commit
acece2dbf3
12 changed files with 416 additions and 120 deletions
|
|
@ -40,7 +40,7 @@ impl WaylandWindowConfig {
|
|||
domain_config: DomainWindowConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
height: domain_config.height,
|
||||
height: domain_config.height.value(),
|
||||
layer: convert_layer(domain_config.layer),
|
||||
margin: domain_config.margin,
|
||||
anchor: convert_anchor(domain_config.anchor),
|
||||
|
|
@ -48,7 +48,7 @@ impl WaylandWindowConfig {
|
|||
domain_config.keyboard_interactivity,
|
||||
),
|
||||
exclusive_zone: domain_config.exclusive_zone,
|
||||
scale_factor: domain_config.scale_factor,
|
||||
scale_factor: domain_config.scale_factor.value(),
|
||||
namespace: domain_config.namespace,
|
||||
component_definition,
|
||||
compilation_result,
|
||||
|
|
|
|||
|
|
@ -2,16 +2,16 @@ use layer_shika_domain::surface_dimensions::SurfaceDimensions;
|
|||
use slint::PhysicalSize;
|
||||
|
||||
pub trait SurfaceDimensionsExt {
|
||||
fn logical_size(&self) -> PhysicalSize;
|
||||
fn physical_size(&self) -> PhysicalSize;
|
||||
fn to_slint_logical_size(&self) -> PhysicalSize;
|
||||
fn to_slint_physical_size(&self) -> PhysicalSize;
|
||||
}
|
||||
|
||||
impl SurfaceDimensionsExt for SurfaceDimensions {
|
||||
fn logical_size(&self) -> PhysicalSize {
|
||||
PhysicalSize::new(self.logical_width, self.logical_height)
|
||||
fn to_slint_logical_size(&self) -> PhysicalSize {
|
||||
PhysicalSize::new(self.logical_width(), self.logical_height())
|
||||
}
|
||||
|
||||
fn physical_size(&self) -> PhysicalSize {
|
||||
PhysicalSize::new(self.physical_width, self.physical_height)
|
||||
fn to_slint_physical_size(&self) -> PhysicalSize {
|
||||
PhysicalSize::new(self.physical_width(), self.physical_height())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use core::result::Result as CoreResult;
|
|||
use layer_shika_domain::errors::DomainError;
|
||||
use layer_shika_domain::ports::windowing::RuntimeStatePort;
|
||||
use layer_shika_domain::surface_dimensions::SurfaceDimensions;
|
||||
use log::info;
|
||||
use log::{error, info};
|
||||
use slint::{LogicalPosition, PhysicalSize, ComponentHandle};
|
||||
use slint::platform::{WindowAdapter, WindowEvent};
|
||||
use slint_interpreter::{ComponentInstance, CompilationResult};
|
||||
|
|
@ -179,22 +179,22 @@ impl WindowState {
|
|||
self.window.set_scale_factor(self.scale_factor);
|
||||
self.window
|
||||
.set_size(slint::WindowSize::Logical(slint::LogicalSize::new(
|
||||
dimensions.logical_width as f32,
|
||||
dimensions.logical_height as f32,
|
||||
dimensions.logical_width() as f32,
|
||||
dimensions.logical_height() as f32,
|
||||
)));
|
||||
}
|
||||
ScalingMode::FractionalOnly => {
|
||||
self.window.set_scale_factor(dimensions.buffer_scale as f32);
|
||||
self.window.set_scale_factor(dimensions.buffer_scale() as f32);
|
||||
self.window
|
||||
.set_size(slint::WindowSize::Logical(slint::LogicalSize::new(
|
||||
dimensions.logical_width as f32,
|
||||
dimensions.logical_height as f32,
|
||||
dimensions.logical_width() as f32,
|
||||
dimensions.logical_height() as f32,
|
||||
)));
|
||||
}
|
||||
ScalingMode::Integer => {
|
||||
self.window.set_scale_factor(self.scale_factor);
|
||||
self.window
|
||||
.set_size(slint::WindowSize::Physical(dimensions.physical_size()));
|
||||
.set_size(slint::WindowSize::Physical(dimensions.to_slint_physical_size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -206,18 +206,18 @@ impl WindowState {
|
|||
self.surface.set_buffer_scale(1);
|
||||
if let Some(viewport) = &self.viewport {
|
||||
viewport.set_destination(
|
||||
dimensions.logical_width as i32,
|
||||
dimensions.logical_height as i32,
|
||||
dimensions.logical_width() as i32,
|
||||
dimensions.logical_height() as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
ScalingMode::FractionalOnly | ScalingMode::Integer => {
|
||||
self.surface.set_buffer_scale(dimensions.buffer_scale);
|
||||
self.surface.set_buffer_scale(dimensions.buffer_scale());
|
||||
}
|
||||
}
|
||||
|
||||
self.layer_surface
|
||||
.set_size(dimensions.logical_width, dimensions.logical_height);
|
||||
.set_size(dimensions.logical_width(), dimensions.logical_height());
|
||||
self.layer_surface.set_exclusive_zone(self.exclusive_zone);
|
||||
self.surface.commit();
|
||||
}
|
||||
|
|
@ -229,17 +229,23 @@ impl WindowState {
|
|||
}
|
||||
|
||||
let scale_factor = self.scale_factor();
|
||||
let dimensions = SurfaceDimensions::calculate(width, height, scale_factor);
|
||||
let dimensions = match SurfaceDimensions::calculate(width, height, scale_factor) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
error!("Failed to calculate surface dimensions: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let scaling_mode = self.determine_scaling_mode();
|
||||
|
||||
info!(
|
||||
"Updating window size: logical {}x{}, physical {}x{}, scale {}, buffer_scale {}, mode {:?}",
|
||||
dimensions.logical_width,
|
||||
dimensions.logical_height,
|
||||
dimensions.physical_width,
|
||||
dimensions.physical_height,
|
||||
dimensions.logical_width(),
|
||||
dimensions.logical_height(),
|
||||
dimensions.physical_width(),
|
||||
dimensions.physical_height(),
|
||||
scale_factor,
|
||||
dimensions.buffer_scale,
|
||||
dimensions.buffer_scale(),
|
||||
scaling_mode
|
||||
);
|
||||
|
||||
|
|
@ -248,8 +254,8 @@ impl WindowState {
|
|||
|
||||
info!("Window physical size: {:?}", self.window.size());
|
||||
|
||||
self.size = dimensions.physical_size();
|
||||
self.logical_size = dimensions.logical_size();
|
||||
self.size = dimensions.to_slint_physical_size();
|
||||
self.logical_size = dimensions.to_slint_logical_size();
|
||||
self.window.request_redraw();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::system::WindowingSystem;
|
|||
use layer_shika_adapters::platform::slint_interpreter::{CompilationResult, Compiler};
|
||||
use layer_shika_domain::errors::DomainError;
|
||||
use layer_shika_domain::prelude::{
|
||||
AnchorEdges, KeyboardInteractivity, Layer, Margins, WindowConfig,
|
||||
AnchorEdges, KeyboardInteractivity, Layer, Margins, ScaleFactor, WindowConfig, WindowHeight,
|
||||
};
|
||||
use spin_on::spin_on;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -135,10 +135,9 @@ impl LayerShika<NeedsComponent> {
|
|||
}
|
||||
|
||||
impl LayerShika<HasComponent> {
|
||||
#[must_use]
|
||||
pub const fn with_height(mut self, height: u32) -> Self {
|
||||
self.config.height = height;
|
||||
self
|
||||
pub fn with_height(mut self, height: u32) -> Result<Self> {
|
||||
self.config.height = WindowHeight::new(height)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
@ -176,10 +175,9 @@ impl LayerShika<HasComponent> {
|
|||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn with_scale_factor(mut self, scale_factor: f32) -> Self {
|
||||
self.config.scale_factor = scale_factor;
|
||||
self
|
||||
pub fn with_scale_factor(mut self, scale_factor: f32) -> Result<Self> {
|
||||
self.config.scale_factor = ScaleFactor::new(scale_factor)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
use crate::dimensions::ScaleFactor;
|
||||
use crate::value_objects::anchor::AnchorEdges;
|
||||
use crate::value_objects::dimensions::WindowHeight;
|
||||
use crate::value_objects::keyboard_interactivity::KeyboardInteractivity;
|
||||
use crate::value_objects::layer::Layer;
|
||||
use crate::value_objects::margins::Margins;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowConfig {
|
||||
pub height: u32,
|
||||
pub height: WindowHeight,
|
||||
pub margin: Margins,
|
||||
pub exclusive_zone: i32,
|
||||
pub scale_factor: f32,
|
||||
pub scale_factor: ScaleFactor,
|
||||
pub namespace: String,
|
||||
pub layer: Layer,
|
||||
pub anchor: AnchorEdges,
|
||||
|
|
@ -19,11 +21,11 @@ impl WindowConfig {
|
|||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
height: 30,
|
||||
height: WindowHeight::default(),
|
||||
margin: Margins::default(),
|
||||
exclusive_zone: -1,
|
||||
namespace: "layer-shika".to_owned(),
|
||||
scale_factor: 1.0,
|
||||
scale_factor: ScaleFactor::default(),
|
||||
layer: Layer::default(),
|
||||
anchor: AnchorEdges::default(),
|
||||
keyboard_interactivity: KeyboardInteractivity::default(),
|
||||
|
|
|
|||
233
domain/src/dimensions.rs
Normal file
233
domain/src/dimensions.rs
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
use crate::errors::DomainError;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct LogicalSize {
|
||||
width: f32,
|
||||
height: f32,
|
||||
}
|
||||
|
||||
impl LogicalSize {
|
||||
pub fn new(width: f32, height: f32) -> Result<Self, DomainError> {
|
||||
if width <= 0.0 || height <= 0.0 {
|
||||
return Err(DomainError::InvalidInput {
|
||||
message: format!("Dimensions must be positive, got width={width}, height={height}"),
|
||||
});
|
||||
}
|
||||
if !width.is_finite() || !height.is_finite() {
|
||||
return Err(DomainError::InvalidInput {
|
||||
message: "Dimensions must be finite values".to_string(),
|
||||
});
|
||||
}
|
||||
Ok(Self { width, height })
|
||||
}
|
||||
|
||||
pub const fn from_raw(width: f32, height: f32) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
|
||||
pub const fn width(&self) -> f32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
pub const fn height(&self) -> f32 {
|
||||
self.height
|
||||
}
|
||||
|
||||
pub fn to_physical(&self, scale_factor: ScaleFactor) -> PhysicalSize {
|
||||
scale_factor.to_physical(*self)
|
||||
}
|
||||
|
||||
pub fn as_tuple(&self) -> (f32, f32) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LogicalSize {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
width: 120.0,
|
||||
height: 120.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PhysicalSize {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
impl PhysicalSize {
|
||||
pub fn new(width: u32, height: u32) -> Result<Self, DomainError> {
|
||||
if width == 0 || height == 0 {
|
||||
return Err(DomainError::InvalidDimensions { width, height });
|
||||
}
|
||||
Ok(Self { width, height })
|
||||
}
|
||||
|
||||
pub const fn from_raw(width: u32, height: u32) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
|
||||
pub const fn width(&self) -> u32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
pub const fn height(&self) -> u32 {
|
||||
self.height
|
||||
}
|
||||
|
||||
pub fn to_logical(&self, scale_factor: ScaleFactor) -> LogicalSize {
|
||||
scale_factor.to_logical(*self)
|
||||
}
|
||||
|
||||
pub fn as_tuple(&self) -> (u32, u32) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PhysicalSize {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
width: 120,
|
||||
height: 120,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ScaleFactor(f32);
|
||||
|
||||
impl ScaleFactor {
|
||||
pub fn new(factor: f32) -> Result<Self, DomainError> {
|
||||
if factor <= 0.0 {
|
||||
return Err(DomainError::InvalidInput {
|
||||
message: format!("Scale factor must be positive, got {factor}"),
|
||||
});
|
||||
}
|
||||
if !factor.is_finite() {
|
||||
return Err(DomainError::InvalidInput {
|
||||
message: "Scale factor must be a finite value".to_string(),
|
||||
});
|
||||
}
|
||||
Ok(Self(factor))
|
||||
}
|
||||
|
||||
pub const fn from_raw(factor: f32) -> Self {
|
||||
Self(factor)
|
||||
}
|
||||
|
||||
pub const fn value(&self) -> f32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
pub fn to_physical(&self, logical: LogicalSize) -> PhysicalSize {
|
||||
let width = (logical.width * self.0).round() as u32;
|
||||
let height = (logical.height * self.0).round() as u32;
|
||||
PhysicalSize::from_raw(width.max(1), height.max(1))
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub fn to_logical(&self, physical: PhysicalSize) -> LogicalSize {
|
||||
let width = physical.width as f32 / self.0;
|
||||
let height = physical.height as f32 / self.0;
|
||||
LogicalSize::from_raw(width, height)
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub fn buffer_scale(&self) -> i32 {
|
||||
self.0.round() as i32
|
||||
}
|
||||
|
||||
pub fn scale_coordinate(&self, logical_coord: f32) -> f32 {
|
||||
logical_coord * self.0
|
||||
}
|
||||
|
||||
pub fn unscale_coordinate(&self, physical_coord: f32) -> f32 {
|
||||
physical_coord / self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ScaleFactor {
|
||||
fn default() -> Self {
|
||||
Self(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct LogicalPosition {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
impl LogicalPosition {
|
||||
pub fn new(x: f32, y: f32) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub const fn x(&self) -> f32 {
|
||||
self.x
|
||||
}
|
||||
|
||||
pub const fn y(&self) -> f32 {
|
||||
self.y
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub fn to_physical(&self, scale_factor: ScaleFactor) -> PhysicalPosition {
|
||||
PhysicalPosition::new(
|
||||
(self.x * scale_factor.value()).round() as i32,
|
||||
(self.y * scale_factor.value()).round() as i32,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_tuple(&self) -> (f32, f32) {
|
||||
(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LogicalPosition {
|
||||
fn default() -> Self {
|
||||
Self { x: 0.0, y: 0.0 }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PhysicalPosition {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
impl PhysicalPosition {
|
||||
pub const fn new(x: i32, y: i32) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub const fn x(&self) -> i32 {
|
||||
self.x
|
||||
}
|
||||
|
||||
pub const fn y(&self) -> i32 {
|
||||
self.y
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub fn to_logical(&self, scale_factor: ScaleFactor) -> LogicalPosition {
|
||||
LogicalPosition::new(
|
||||
self.x as f32 / scale_factor.value(),
|
||||
self.y as f32 / scale_factor.value(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_tuple(&self) -> (i32, i32) {
|
||||
(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for PhysicalPosition {
|
||||
fn default() -> Self {
|
||||
Self { x: 0, y: 0 }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
pub mod config;
|
||||
pub mod dimensions;
|
||||
pub mod entities;
|
||||
pub mod errors;
|
||||
pub mod ports;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
#![allow(clippy::pub_use)]
|
||||
|
||||
pub use crate::config::WindowConfig;
|
||||
pub use crate::dimensions::{
|
||||
LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, ScaleFactor,
|
||||
};
|
||||
pub use crate::entities::component::UiComponentHandle;
|
||||
pub use crate::entities::surface::SurfaceHandle;
|
||||
pub use crate::entities::window::WindowHandle;
|
||||
|
|
|
|||
|
|
@ -1,30 +1,62 @@
|
|||
use crate::dimensions::{LogicalSize, PhysicalSize, ScaleFactor};
|
||||
use crate::errors::Result;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SurfaceDimensions {
|
||||
pub logical_width: u32,
|
||||
pub logical_height: u32,
|
||||
pub physical_width: u32,
|
||||
pub physical_height: u32,
|
||||
pub buffer_scale: i32,
|
||||
logical: LogicalSize,
|
||||
physical: PhysicalSize,
|
||||
scale_factor: ScaleFactor,
|
||||
}
|
||||
|
||||
impl SurfaceDimensions {
|
||||
#[must_use]
|
||||
#[allow(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cast_precision_loss
|
||||
)]
|
||||
pub fn calculate(logical_width: u32, logical_height: u32, scale_factor: f32) -> Self {
|
||||
let physical_width = (logical_width as f32 * scale_factor).round() as u32;
|
||||
let physical_height = (logical_height as f32 * scale_factor).round() as u32;
|
||||
let buffer_scale = scale_factor.round() as i32;
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub fn calculate(
|
||||
logical_width: u32,
|
||||
logical_height: u32,
|
||||
scale_factor: f32,
|
||||
) -> Result<Self> {
|
||||
let logical = LogicalSize::new(logical_width as f32, logical_height as f32)?;
|
||||
let scale = ScaleFactor::new(scale_factor)?;
|
||||
let physical = scale.to_physical(logical);
|
||||
|
||||
Self {
|
||||
logical_width,
|
||||
logical_height,
|
||||
physical_width,
|
||||
physical_height,
|
||||
buffer_scale,
|
||||
Ok(Self {
|
||||
logical,
|
||||
physical,
|
||||
scale_factor: scale,
|
||||
})
|
||||
}
|
||||
|
||||
pub const fn logical_size(&self) -> LogicalSize {
|
||||
self.logical
|
||||
}
|
||||
|
||||
pub const fn physical_size(&self) -> PhysicalSize {
|
||||
self.physical
|
||||
}
|
||||
|
||||
pub const fn scale_factor(&self) -> ScaleFactor {
|
||||
self.scale_factor
|
||||
}
|
||||
|
||||
pub fn buffer_scale(&self) -> i32 {
|
||||
self.scale_factor.buffer_scale()
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
pub fn logical_width(&self) -> u32 {
|
||||
self.logical.width() as u32
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
pub fn logical_height(&self) -> u32 {
|
||||
self.logical.height() as u32
|
||||
}
|
||||
|
||||
pub fn physical_width(&self) -> u32 {
|
||||
self.physical.width()
|
||||
}
|
||||
|
||||
pub fn physical_height(&self) -> u32 {
|
||||
self.physical.height()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,23 @@
|
|||
#[derive(Debug, Clone, Copy)]
|
||||
use crate::errors::{DomainError, Result};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct WindowHeight(u32);
|
||||
|
||||
impl WindowHeight {
|
||||
#[must_use]
|
||||
pub const fn new(height: u32) -> Self {
|
||||
pub fn new(height: u32) -> Result<Self> {
|
||||
if height == 0 {
|
||||
return Err(DomainError::InvalidDimensions {
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
}
|
||||
Ok(Self(height))
|
||||
}
|
||||
|
||||
pub const fn from_raw(height: u32) -> Self {
|
||||
Self(height)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn value(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
|
@ -18,3 +28,11 @@ impl Default for WindowHeight {
|
|||
Self(30)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for WindowHeight {
|
||||
type Error = DomainError;
|
||||
|
||||
fn try_from(height: u32) -> Result<Self> {
|
||||
Self::new(height)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,16 @@
|
|||
use super::popup_positioning_mode::PopupPositioningMode;
|
||||
use crate::dimensions::{LogicalPosition, LogicalSize};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PopupConfig {
|
||||
reference_x: f32,
|
||||
reference_y: f32,
|
||||
width: f32,
|
||||
height: f32,
|
||||
reference_position: LogicalPosition,
|
||||
popup_size: LogicalSize,
|
||||
output_size: LogicalSize,
|
||||
positioning_mode: PopupPositioningMode,
|
||||
output_width: f32,
|
||||
output_height: f32,
|
||||
}
|
||||
|
||||
impl PopupConfig {
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
pub fn new(
|
||||
reference_x: f32,
|
||||
reference_y: f32,
|
||||
width: f32,
|
||||
|
|
@ -23,62 +20,68 @@ impl PopupConfig {
|
|||
output_height: f32,
|
||||
) -> Self {
|
||||
Self {
|
||||
reference_x,
|
||||
reference_y,
|
||||
width,
|
||||
height,
|
||||
reference_position: LogicalPosition::new(reference_x, reference_y),
|
||||
popup_size: LogicalSize::from_raw(width, height),
|
||||
output_size: LogicalSize::from_raw(output_width, output_height),
|
||||
positioning_mode,
|
||||
output_width,
|
||||
output_height,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn reference_position(&self) -> LogicalPosition {
|
||||
self.reference_position
|
||||
}
|
||||
|
||||
pub const fn reference_x(&self) -> f32 {
|
||||
self.reference_x
|
||||
self.reference_position.x()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn reference_y(&self) -> f32 {
|
||||
self.reference_y
|
||||
self.reference_position.y()
|
||||
}
|
||||
|
||||
pub const fn popup_size(&self) -> LogicalSize {
|
||||
self.popup_size
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn width(&self) -> f32 {
|
||||
self.width
|
||||
self.popup_size.width()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn height(&self) -> f32 {
|
||||
self.height
|
||||
self.popup_size.height()
|
||||
}
|
||||
|
||||
pub const fn output_size(&self) -> LogicalSize {
|
||||
self.output_size
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn positioning_mode(&self) -> PopupPositioningMode {
|
||||
self.positioning_mode
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn calculated_top_left_position(&self) -> LogicalPosition {
|
||||
LogicalPosition::new(self.calculated_top_left_x(), self.calculated_top_left_y())
|
||||
}
|
||||
|
||||
pub fn calculated_top_left_x(&self) -> f32 {
|
||||
let unclamped_x = if self.positioning_mode.center_x() {
|
||||
self.reference_x - (self.width / 2.0)
|
||||
self.reference_x() - (self.width() / 2.0)
|
||||
} else {
|
||||
self.reference_x
|
||||
self.reference_x()
|
||||
};
|
||||
|
||||
let max_x = self.output_width - self.width;
|
||||
let max_x = self.output_size.width() - self.width();
|
||||
unclamped_x.max(0.0).min(max_x)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn calculated_top_left_y(&self) -> f32 {
|
||||
let unclamped_y = if self.positioning_mode.center_y() {
|
||||
self.reference_y - (self.height / 2.0)
|
||||
self.reference_y() - (self.height() / 2.0)
|
||||
} else {
|
||||
self.reference_y
|
||||
self.reference_y()
|
||||
};
|
||||
|
||||
let max_y = self.output_height - self.height;
|
||||
let max_y = self.output_size.height() - self.height();
|
||||
unclamped_y.max(0.0).min(max_y)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,42 @@
|
|||
use crate::dimensions::LogicalSize;
|
||||
use crate::errors::{DomainError, Result};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct PopupDimensions {
|
||||
width: f32,
|
||||
height: f32,
|
||||
size: LogicalSize,
|
||||
}
|
||||
|
||||
impl PopupDimensions {
|
||||
#[must_use]
|
||||
pub const fn new(width: f32, height: f32) -> Self {
|
||||
Self { width, height }
|
||||
pub fn new(width: f32, height: f32) -> Result<Self> {
|
||||
let size = LogicalSize::new(width, height)?;
|
||||
Ok(Self { size })
|
||||
}
|
||||
|
||||
pub const fn from_logical(size: LogicalSize) -> Self {
|
||||
Self { size }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn width(&self) -> f32 {
|
||||
self.width
|
||||
self.size.width()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn height(&self) -> f32 {
|
||||
self.height
|
||||
self.size.height()
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
if self.width <= 0.0 || self.height <= 0.0 {
|
||||
return Err(DomainError::Configuration {
|
||||
message: format!(
|
||||
"Invalid popup dimensions: width={}, height={}. Both must be positive.",
|
||||
self.width, self.height
|
||||
),
|
||||
});
|
||||
pub const fn logical_size(&self) -> LogicalSize {
|
||||
self.size
|
||||
}
|
||||
Ok(())
|
||||
|
||||
pub fn as_tuple(&self) -> (f32, f32) {
|
||||
self.size.as_tuple()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PopupDimensions {
|
||||
fn default() -> Self {
|
||||
Self::new(120.0, 120.0)
|
||||
impl TryFrom<(f32, f32)> for PopupDimensions {
|
||||
type Error = DomainError;
|
||||
|
||||
fn try_from((width, height): (f32, f32)) -> Result<Self> {
|
||||
Self::new(width, height)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue