ltk/platform/wayland/
window_fit.rsuse smithay_client_toolkit::reexports::client::protocol::wl_output::WlOutput;
use smithay_client_toolkit::shell::xdg::window::Window;
use crate::app::App;
use crate::event_loop::AppData;
pub( crate ) const OUTPUT_SHARE: f32 = 0.9;
pub( crate ) fn output_share( output: ( u32, u32 ) ) -> ( u32, u32 )
{
( ( output.0 as f32 * OUTPUT_SHARE ) as u32, ( output.1 as f32 * OUTPUT_SHARE ) as u32 )
}
pub( crate ) fn fit_within( size: ( u32, u32 ), bounds: ( u32, u32 ) ) -> ( u32, u32 )
{
let ( w, h ) = size;
let ( bw, bh ) = bounds;
if w == 0 || h == 0 || bw == 0 || bh == 0 { return size; }
if w <= bw && h <= bh { return size; }
let scale = ( bw as f32 / w as f32 ).min( bh as f32 / h as f32 );
( ( ( w as f32 * scale ) as u32 ).max( 1 ), ( ( h as f32 * scale ) as u32 ).max( 1 ) )
}
impl<A: App> AppData<A>
{
pub( crate ) fn fit_size_hint( &mut self, window: &Window, suggested: Option<( u32, u32 )> ) -> bool
{
if !std::mem::take( &mut self.platform.pending_size_hint_fit ) { return false; }
let Some( hint ) = self.app.window_size_hint() else { return false };
let bounds = suggested.filter( |b| b.0 > 0 && b.1 > 0 ).or_else( || self.output_bounds() );
let Some( bounds ) = bounds else { return false };
let fitted = fit_within( hint, bounds );
if fitted == hint { return false; }
window.set_min_size( Some( fitted ) );
if !self.app.window_resizable() { window.set_max_size( Some( fitted ) ); }
self.platform.fitted_size_hint = Some( fitted );
true
}
fn output_bounds( &self ) -> Option<( u32, u32 )>
{
self.output_logical_size( None ).map( output_share )
}
fn output_logical_size( &self, preferred: Option<&WlOutput> ) -> Option<( u32, u32 )>
{
let info = preferred
.or( self.platform.main_output.as_ref() )
.and_then( |o| self.platform.output_state.info( o ) )
.or_else( || self.platform.output_state.outputs().find_map( |o| self.platform.output_state.info( &o ) ) )?;
let ( w, h ) = info.logical_size.or_else( ||
{
let mode = info.modes.iter().find( |m| m.current )?;
let scale = info.scale_factor.max( 1 );
Some( ( mode.dimensions.0 / scale, mode.dimensions.1 / scale ) )
} )?;
( w > 0 && h > 0 ).then_some( ( w as u32, h as u32 ) )
}
pub( crate ) fn publish_output_size( &self, preferred: Option<&WlOutput> )
{
let ( w, h ) = self.output_logical_size( preferred ).unwrap_or( ( 0, 0 ) );
crate::types::set_output_logical_size( w, h );
}
}
#[ cfg( test ) ]
mod tests
{
use super::*;
#[ test ]
fn a_size_that_fits_is_kept()
{
assert_eq!( fit_within( ( 800, 600 ), ( 1920, 1080 ) ), ( 800, 600 ) );
assert_eq!( fit_within( ( 1920, 1080 ), ( 1920, 1080 ) ), ( 1920, 1080 ) );
}
#[ test ]
fn a_tall_window_shrinks_to_the_height_and_keeps_its_shape()
{
assert_eq!( fit_within( ( 720, 1440 ), ( 1920, 1080 ) ), ( 540, 1080 ) );
}
#[ test ]
fn a_wide_window_shrinks_to_the_width()
{
assert_eq!( fit_within( ( 1600, 900 ), ( 800, 1280 ) ), ( 800, 450 ) );
}
#[ test ]
fn degenerate_input_is_left_alone()
{
assert_eq!( fit_within( ( 720, 1440 ), ( 0, 1080 ) ), ( 720, 1440 ) );
assert_eq!( fit_within( ( 0, 1440 ), ( 1920, 1080 ) ), ( 0, 1440 ) );
}
}