state: Decide panel arrangement

Combines arrangement with layout to get panel contents as outcome.

Includes some path syntax changes for 2018 compatibility.
This commit is contained in:
Dorota Czaplejewicz
2022-06-03 17:27:47 +00:00
parent 590cd71f49
commit b634e2bfa4
3 changed files with 120 additions and 68 deletions

View File

@ -8,16 +8,26 @@ use std::time::Duration;
use crate::outputs::OutputId;
use crate::panel::PixelSize;
use crate::layout::ArrangementKind;
/// The keyboard should hide after this has elapsed to prevent flickering.
pub const HIDING_TIMEOUT: Duration = Duration::from_millis(200);
/// Panel contents
#[derive(PartialEq, Clone, Debug)]
pub struct Contents {
pub name: String,
pub kind: ArrangementKind,
pub overlay_name: Option<String>,
}
/// The outwardly visible state of visibility
#[derive(PartialEq, Debug, Clone)]
pub enum Outcome {
Visible {
output: OutputId,
height: PixelSize,
contents: Contents,
},
Hidden,
}

View File

@ -12,6 +12,7 @@ use std::convert::TryFrom;
use super::{ Error, LoadError };
use super::parsing;
use crate::layout;
use ::layout::ArrangementKind;
use ::logging;
use ::util::c::as_str;
@ -33,8 +34,10 @@ pub mod c {
name: *const c_char, // name of the keyboard
type_: u32, // type like Wide
variant: u32, // purpose variant like numeric, terminal...
overlay: *const c_char, // the overlay (looking for "terminal")
) -> *mut ::layout::Layout {
// Overlay forces a variant other than specified
// (typically "terminal", "emoji")
overlay: *const c_char,
) -> *mut layout::Layout {
let type_ = match type_ {
0 => ArrangementKind::Base,
1 => ArrangementKind::Wide,
@ -60,8 +63,10 @@ pub mod c {
other => Some(other),
};
dbg!(&name, type_, variant, overlay_str);
let (kind, layout) = load_layout_data_with_fallback(&name, type_, variant, overlay_str);
let layout = ::layout::Layout::new(layout, kind, variant);
let layout = layout::Layout::new(layout, kind, variant);
Box::into_raw(Box::new(layout))
}
}
@ -265,7 +270,7 @@ fn load_layout_data_with_fallback(
kind: ArrangementKind,
purpose: ContentPurpose,
overlay: Option<&str>,
) -> (ArrangementKind, ::layout::LayoutData) {
) -> (ArrangementKind, layout::LayoutData) {
// Build the path to the right keyboard layout subdirectory
let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")

View File

@ -8,6 +8,7 @@
use crate::animation;
use crate::debug;
use crate::imservice::{ ContentHint, ContentPurpose };
use crate::layout::ArrangementKind;
use crate::main::Commands;
use crate::outputs;
use crate::outputs::{Millimeter, OutputId, OutputState};
@ -113,9 +114,8 @@ pub mod visibility {
/// The outwardly visible state.
#[derive(Clone, Debug)]
pub struct Outcome {
pub visibility: animation::Outcome,
pub panel: animation::Outcome,
pub im: InputMethod,
pub layout: LayoutChoice,
}
impl Outcome {
@ -127,13 +127,13 @@ impl Outcome {
pub fn get_commands_to_reach(&self, new_state: &Self) -> Commands {
let layout_hint_set = match new_state {
Outcome {
visibility: animation::Outcome::Visible{..},
panel: animation::Outcome::Visible{..},
im: InputMethod::Active(hints),
..
} => Some(hints.clone()),
Outcome {
visibility: animation::Outcome::Visible{..},
panel: animation::Outcome::Visible{..},
im: InputMethod::InactiveSince(_),
..
} => Some(InputMethodDetails {
@ -142,13 +142,13 @@ impl Outcome {
}),
Outcome {
visibility: animation::Outcome::Hidden,
panel: animation::Outcome::Hidden,
..
} => None,
};
// FIXME: handle switching outputs
let (dbus_visible_set, panel_visibility) = match new_state.visibility {
animation::Outcome::Visible{output, height}
let (dbus_visible_set, panel_visibility) = match new_state.panel {
animation::Outcome::Visible{output, height, ..}
=> (Some(true), Some(panel::Command::Show{output, height})),
animation::Outcome::Hidden => (Some(false), Some(panel::Command::Hide)),
};
@ -325,7 +325,9 @@ Outcome:
state
}
fn get_preferred_height(output: &OutputState) -> Option<PixelSize> {
fn get_preferred_height_and_arrangement(output: &OutputState)
-> Option<(PixelSize, ArrangementKind)>
{
output.get_pixel_size()
.map(|px_size| {
// Assume isotropy.
@ -353,7 +355,6 @@ Outcome:
// TODO: calculate based on selected layout
const ROW_COUNT: u32 = 4;
let height = {
let ideal_height = IDEAL_TARGET_SIZE * ROW_COUNT as i32;
let ideal_height_px = (ideal_height * density).ceil().0 as u32;
@ -368,45 +369,76 @@ Outcome:
}
.as_scaled_ceiling();
let height_as_widths = {
if abstract_width < 540 {
// Normal
let (arrangement, height_as_widths) = {
if abstract_width < 540 {(
ArrangementKind::Base,
Rational {
numerator: 210,
denominator: 360,
}
} else {
// Wide
},
)} else {(
ArrangementKind::Wide,
Rational {
numerator: 172,
denominator: 540,
}
}
)}
};
cmp::min(
let height
= cmp::min(
ideal_height_px,
(height_as_widths * px_size.width as i32).ceil() as u32,
)
};
);
(
PixelSize {
scale_factor: output.scale as u32,
pixels: cmp::min(height, px_size.height / 2),
}
},
arrangement,
)
})
}
/// Returns layout name, overlay name
fn get_layout_names(&self) -> (String, Option<String>) {
(
String::from(match &self.overlay_layout {
Some(popover::LayoutId::System { name, .. }) => name,
_ => &self.layout_choice.name,
}),
match &self.overlay_layout {
Some(popover::LayoutId::Local(name)) => Some(name.clone()),
_ => None,
},
)
}
pub fn get_outcome(&self, now: Instant) -> Outcome {
// FIXME: include physical keyboard presence
Outcome {
visibility: match self.preferred_output {
panel: match self.preferred_output {
None => animation::Outcome::Hidden,
Some(output) => {
// Hoping that this will get optimized out on branches not using `visible`.
let height = Self::get_preferred_height(self.outputs.get(&output).unwrap())
.unwrap_or(PixelSize{pixels: 0, scale_factor: 1});
let (height, arrangement) = Self::get_preferred_height_and_arrangement(self.outputs.get(&output).unwrap())
.unwrap_or((
PixelSize{pixels: 0, scale_factor: 1},
ArrangementKind::Base,
));
let (layout_name, overlay) = self.get_layout_names();
// TODO: Instead of setting size to 0 when the output is invalid,
// simply go invisible.
let visible = animation::Outcome::Visible{ output, height };
let visible = animation::Outcome::Visible{
output,
height,
contents: animation::Contents {
kind: arrangement,
name: layout_name,
overlay_name: overlay,
}
};
match (self.physical_keyboard, self.visibility_override) {
(_, visibility::State::ForcedHidden) => animation::Outcome::Hidden,
@ -423,7 +455,6 @@ Outcome:
}
},
im: self.im.clone(),
layout: self.layout_choice.clone(),
}
}
@ -499,7 +530,7 @@ pub mod test {
for _i in 0..100 {
now += Duration::from_millis(1);
assert_matches!(
state.get_outcome(now).visibility,
state.get_outcome(now).panel,
animation::Outcome::Visible{..},
"Hidden when it should remain visible: {:?}",
now.saturating_duration_since(start),
@ -508,7 +539,10 @@ pub mod test {
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
assert_matches!(state.get_outcome(now).visibility, animation::Outcome::Visible{..});
assert_matches!(
state.get_outcome(now).panel,
animation::Outcome::Visible{..}
);
}
/// Make sure that hiding works when input method goes away
@ -525,7 +559,7 @@ pub mod test {
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
while let animation::Outcome::Visible{..} = state.get_outcome(now).visibility {
while let animation::Outcome::Visible{..} = state.get_outcome(now).panel {
now += Duration::from_millis(1);
assert!(
now < start + Duration::from_millis(250),
@ -555,7 +589,7 @@ pub mod test {
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
while let animation::Outcome::Visible{..} = state.get_outcome(now).visibility {
while let animation::Outcome::Visible{..} = state.get_outcome(now).panel {
now += Duration::from_millis(1);
assert!(
now < start + Duration::from_millis(250),
@ -568,7 +602,7 @@ pub mod test {
for _i in 0..1000 {
now += Duration::from_millis(1);
assert_eq!(
state.get_outcome(now).visibility,
state.get_outcome(now).panel,
animation::Outcome::Hidden,
"Appeared unnecessarily: {:?}",
now.saturating_duration_since(start),
@ -590,7 +624,7 @@ pub mod test {
let state = state.apply_event(Event::Visibility(visibility::Event::ForceVisible), now);
assert_matches!(
state.get_outcome(now).visibility,
state.get_outcome(now).panel,
animation::Outcome::Visible{..},
"Failed to show: {:?}",
now.saturating_duration_since(start),
@ -603,7 +637,7 @@ pub mod test {
now += Duration::from_secs(1);
assert_eq!(
state.get_outcome(now).visibility,
state.get_outcome(now).panel,
animation::Outcome::Hidden,
"Failed to release forced visibility: {:?}",
now.saturating_duration_since(start),
@ -624,7 +658,7 @@ pub mod test {
let state = state.apply_event(Event::PhysicalKeyboard(Presence::Present), now);
assert_eq!(
state.get_outcome(now).visibility,
state.get_outcome(now).panel,
animation::Outcome::Hidden,
"Failed to hide: {:?}",
now.saturating_duration_since(start),
@ -636,7 +670,7 @@ pub mod test {
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
assert_eq!(
state.get_outcome(now).visibility,
state.get_outcome(now).panel,
animation::Outcome::Hidden,
"Failed to remain hidden: {:?}",
now.saturating_duration_since(start),
@ -646,7 +680,7 @@ pub mod test {
let state = state.apply_event(Event::PhysicalKeyboard(Presence::Missing), now);
assert_matches!(
state.get_outcome(now).visibility,
state.get_outcome(now).panel,
animation::Outcome::Visible{..},
"Failed to appear: {:?}",
now.saturating_duration_since(start),
@ -658,7 +692,7 @@ pub mod test {
fn size_l5() {
use crate::outputs::{Mode, Geometry, c, Size};
assert_eq!(
Application::get_preferred_height(&OutputState {
Application::get_preferred_height_and_arrangement(&OutputState {
current_mode: Some(Mode {
width: 720,
height: 1440,
@ -672,10 +706,13 @@ pub mod test {
}),
scale: 2,
}),
Some(PixelSize {
Some((
PixelSize {
scale_factor: 2,
pixels: 420,
}),
},
ArrangementKind::Base,
)),
);
}
}