diff --git a/src/animation.rs b/src/animation.rs index 2ca9b06f..b6e6c060 100644 --- a/src/animation.rs +++ b/src/animation.rs @@ -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, +} + /// The outwardly visible state of visibility #[derive(PartialEq, Debug, Clone)] pub enum Outcome { Visible { output: OutputId, height: PixelSize, + contents: Contents, }, Hidden, } diff --git a/src/data/loading.rs b/src/data/loading.rs index 20360f43..63e372ba 100644 --- a/src/data/loading.rs +++ b/src/data/loading.rs @@ -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") diff --git a/src/state.rs b/src/state.rs index 8d20c337..19f58bcd 100644 --- a/src/state.rs +++ b/src/state.rs @@ -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 { + fn get_preferred_height_and_arrangement(output: &OutputState) + -> Option<(PixelSize, ArrangementKind)> + { output.get_pixel_size() .map(|px_size| { // Assume isotropy. @@ -353,61 +355,91 @@ 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; + let ideal_height = IDEAL_TARGET_SIZE * ROW_COUNT as i32; + let ideal_height_px = (ideal_height * density).ceil().0 as u32; - // Reduce height to match what the layout can fill. - // For this, we need to guess if normal or wide will be picked up. - // This must match `eek_gtk_keyboard.c::get_type`. - // TODO: query layout database and choose one directly - let abstract_width - = PixelSize { - scale_factor: output.scale as u32, - pixels: px_size.width, - } - .as_scaled_ceiling(); + // Reduce height to match what the layout can fill. + // For this, we need to guess if normal or wide will be picked up. + // This must match `eek_gtk_keyboard.c::get_type`. + // TODO: query layout database and choose one directly + let abstract_width + = PixelSize { + scale_factor: output.scale as u32, + pixels: px_size.width, + } + .as_scaled_ceiling(); - let height_as_widths = { - if abstract_width < 540 { - // Normal - Rational { - numerator: 210, - denominator: 360, - } - } else { - // Wide - Rational { - numerator: 172, - denominator: 540, - } + let (arrangement, height_as_widths) = { + if abstract_width < 540 {( + ArrangementKind::Base, + Rational { + numerator: 210, + denominator: 360, + }, + )} 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), - } + ); + + ( + 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::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, (_, visibility::State::ForcedVisible) => visible, @@ -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 { - scale_factor: 2, - pixels: 420, - }), + Some(( + PixelSize { + scale_factor: 2, + pixels: 420, + }, + ArrangementKind::Base, + )), ); } }