Merge branch 'layouts' into 'master'
state: Select the layout See merge request World/Phosh/squeekboard!553
This commit is contained in:
23
src/actors/mod.rs
Normal file
23
src/actors/mod.rs
Normal file
@ -0,0 +1,23 @@
|
||||
/* Copyright (C) 2022 Purism SPC
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
*/
|
||||
|
||||
/*! Actors are parts of Squeekboard containing state independent from the main application state.
|
||||
|
||||
Because main application state is meant to be immutable,
|
||||
it cannot be referenced directly by pieces of logic
|
||||
interacting with the environment.
|
||||
|
||||
Such impure logic is split away (actor's logic)
|
||||
and combined with relevant pieces of state (actor state),
|
||||
thus preserving the purity (and sometimes simplicity) of the main state.
|
||||
|
||||
Actors can communicate with the main state by sending it messages,
|
||||
and by receiving updates from it.
|
||||
*/
|
||||
|
||||
// TODO: move crate::panel into crate::actors::panel.
|
||||
// Panel contains state and logic to protect the main state from getting flooded
|
||||
// with low-level wayland and gtk sizing events.
|
||||
|
||||
pub mod popover;
|
||||
40
src/actors/popover.rs
Normal file
40
src/actors/popover.rs
Normal file
@ -0,0 +1,40 @@
|
||||
/* Copyright (C) 2022 Purism SPC
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
*/
|
||||
|
||||
/*! The popover is opened directly by the GTK surface,
|
||||
without bouncing click events off the main state.
|
||||
Then it must accurately show which layout has been selected.
|
||||
It can get the system layout directly from gsettings on open,
|
||||
but it cannot get the user-selected overlay, because it's stored in state.
|
||||
|
||||
To solve this, overlay will be cached in the popover actor,
|
||||
and updated by main state every time it changes.
|
||||
*/
|
||||
|
||||
pub mod c {
|
||||
use super::*;
|
||||
use crate::util::c::Wrapped;
|
||||
/// The mutable instance of state
|
||||
pub type Actor = Wrapped<State>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct State {
|
||||
pub overlay: Option<String>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
Self { overlay: None }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_overlay(
|
||||
actor: &c::Actor,
|
||||
overlay: Option<String>,
|
||||
) {
|
||||
let actor = actor.clone_ref();
|
||||
let mut actor = actor.borrow_mut();
|
||||
actor.overlay = overlay;
|
||||
}
|
||||
@ -6,18 +6,30 @@
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::imservice::ContentPurpose;
|
||||
use crate::layout::ArrangementKind;
|
||||
use crate::outputs::OutputId;
|
||||
use crate::panel::PixelSize;
|
||||
|
||||
/// The keyboard should hide after this has elapsed to prevent flickering.
|
||||
pub const HIDING_TIMEOUT: Duration = Duration::from_millis(200);
|
||||
|
||||
/// Description of parameters which influence panel contents
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct Contents {
|
||||
pub name: String,
|
||||
pub kind: ArrangementKind,
|
||||
pub overlay_name: Option<String>,
|
||||
pub purpose: ContentPurpose,
|
||||
}
|
||||
|
||||
/// The outwardly visible state of visibility
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Outcome {
|
||||
Visible {
|
||||
output: OutputId,
|
||||
height: PixelSize,
|
||||
contents: Contents,
|
||||
},
|
||||
Hidden,
|
||||
}
|
||||
|
||||
@ -7,64 +7,16 @@
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use super::{ Error, LoadError };
|
||||
use super::parsing;
|
||||
|
||||
use ::layout::ArrangementKind;
|
||||
use ::logging;
|
||||
use ::util::c::as_str;
|
||||
use ::xdg;
|
||||
use ::imservice::ContentPurpose;
|
||||
use crate::layout;
|
||||
use crate::layout::ArrangementKind;
|
||||
use crate::logging;
|
||||
use crate::xdg;
|
||||
use crate::imservice::ContentPurpose;
|
||||
|
||||
// traits, derives
|
||||
use ::logging::Warn;
|
||||
|
||||
|
||||
/// Gathers stuff defined in C or called by C
|
||||
pub mod c {
|
||||
use super::*;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_load_layout(
|
||||
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 {
|
||||
let type_ = match type_ {
|
||||
0 => ArrangementKind::Base,
|
||||
1 => ArrangementKind::Wide,
|
||||
_ => panic!("Bad enum value"),
|
||||
};
|
||||
|
||||
let name = as_str(&name)
|
||||
.expect("Bad layout name")
|
||||
.expect("Empty layout name");
|
||||
|
||||
let variant = ContentPurpose::try_from(variant)
|
||||
.or_print(
|
||||
logging::Problem::Warning,
|
||||
"Received invalid purpose value",
|
||||
)
|
||||
.unwrap_or(ContentPurpose::Normal);
|
||||
|
||||
let overlay_str = as_str(&overlay)
|
||||
.expect("Bad overlay name")
|
||||
.expect("Empty overlay name");
|
||||
let overlay_str = match overlay_str {
|
||||
"" => None,
|
||||
other => Some(other),
|
||||
};
|
||||
|
||||
let (kind, layout) = load_layout_data_with_fallback(&name, type_, variant, overlay_str);
|
||||
let layout = ::layout::Layout::new(layout, kind, variant);
|
||||
Box::into_raw(Box::new(layout))
|
||||
}
|
||||
}
|
||||
|
||||
const FALLBACK_LAYOUT_NAME: &str = "us";
|
||||
|
||||
@ -265,7 +217,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")
|
||||
@ -300,6 +252,17 @@ fn load_layout_data_with_fallback(
|
||||
panic!("No useful layout found!");
|
||||
}
|
||||
|
||||
pub fn load_layout(
|
||||
name: String,
|
||||
kind: ArrangementKind,
|
||||
variant: ContentPurpose,
|
||||
overlay: Option<String>,
|
||||
) -> layout::Layout {
|
||||
let overlay = overlay.as_ref().map(String::as_str);
|
||||
let (found_kind, layout)
|
||||
= load_layout_data_with_fallback(&name, kind, variant, overlay);
|
||||
layout::Layout::new(layout, found_kind, variant)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
/*! Combined module for dealing with layout files */
|
||||
|
||||
mod loading;
|
||||
pub mod loading;
|
||||
pub mod parsing;
|
||||
|
||||
use std::io;
|
||||
|
||||
@ -39,7 +39,9 @@ type UISender = glib::Sender<Commands>;
|
||||
/// It sends outcomes to the glib main loop using a channel.
|
||||
/// The outcomes are applied by the UI end of the channel in the `main` module.
|
||||
// This could still be reasonably tested,
|
||||
// by creating a glib::Sender and checking what messages it receives.
|
||||
/// by creating a glib::Sender and checking what messages it receives.
|
||||
// This can/should be abstracted over Event and Commands,
|
||||
// so that the C call-ins can be thrown away from here and defined near events.
|
||||
#[derive(Clone)]
|
||||
pub struct Threaded {
|
||||
thread: Sender,
|
||||
@ -108,8 +110,11 @@ mod c {
|
||||
use super::*;
|
||||
|
||||
use crate::state::Presence;
|
||||
use crate::state::LayoutChoice;
|
||||
use crate::state::visibility;
|
||||
use crate::util;
|
||||
use crate::util::c::Wrapped;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
@ -140,4 +145,28 @@ mod c {
|
||||
sender.send(Event::PhysicalKeyboard(state))
|
||||
.or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C"
|
||||
fn squeek_state_send_layout_set(
|
||||
sender: Wrapped<Threaded>,
|
||||
name: *const c_char,
|
||||
source: *const c_char,
|
||||
// TODO: use when synthetic events are needed
|
||||
_timestamp: u32,
|
||||
) {
|
||||
let sender = sender.clone_ref();
|
||||
let sender = sender.borrow();
|
||||
let string_or_empty = |v| String::from(
|
||||
util::c::as_str(v)
|
||||
.unwrap_or(Some(""))
|
||||
.unwrap_or("")
|
||||
);
|
||||
sender
|
||||
.send(Event::LayoutChoice(LayoutChoice {
|
||||
name: string_or_empty(&name),
|
||||
source: string_or_empty(&source).into(),
|
||||
}))
|
||||
.or_warn(&mut logging::Print, logging::Problem::Warning, "Can't send to state manager");
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,7 +226,7 @@ bitflags!{
|
||||
/// use rs::imservice::ContentPurpose;
|
||||
/// assert_eq!(ContentPurpose::Alpha as u32, 1);
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum ContentPurpose {
|
||||
Normal = 0,
|
||||
Alpha = 1,
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
#include "eek/eek-gtk-keyboard.h"
|
||||
#include "eek/eek-renderer.h"
|
||||
#include "eek/eek-types.h"
|
||||
#include "src/main.h"
|
||||
#include "src/popover.h"
|
||||
#include "src/submission.h"
|
||||
#include "virtual-keyboard-unstable-v1-client-protocol.h"
|
||||
#include "text-input-unstable-v3-client-protocol.h"
|
||||
@ -40,7 +42,8 @@ void squeek_layout_release(struct squeek_layout *layout,
|
||||
struct submission *submission,
|
||||
struct transformation widget_to_layout,
|
||||
uint32_t timestamp,
|
||||
EekboardContextService *manager,
|
||||
struct squeek_popover *popover,
|
||||
struct squeek_state_manager *state,
|
||||
EekGtkKeyboard *ui_keyboard);
|
||||
void squeek_layout_release_all_only(struct squeek_layout *layout,
|
||||
struct submission *submission,
|
||||
@ -54,7 +57,8 @@ void squeek_layout_drag(struct squeek_layout *layout,
|
||||
struct submission *submission,
|
||||
double x_widget, double y_widget,
|
||||
struct transformation widget_to_layout,
|
||||
uint32_t timestamp, EekboardContextService *manager,
|
||||
uint32_t timestamp, struct squeek_popover *popover,
|
||||
struct squeek_state_manager *state,
|
||||
EekGtkKeyboard *ui_keyboard);
|
||||
void squeek_layout_draw_all_changed(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr, struct submission *submission);
|
||||
void squeek_draw_layout_base_view(struct squeek_layout *layout, EekRenderer* renderer, cairo_t *cr);
|
||||
|
||||
@ -25,31 +25,36 @@ use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use std::vec::Vec;
|
||||
|
||||
use ::action::Action;
|
||||
use ::drawing;
|
||||
use ::float_ord::FloatOrd;
|
||||
use ::keyboard::KeyState;
|
||||
use ::logging;
|
||||
use ::manager;
|
||||
use ::submission::{ Submission, SubmitData, Timestamp };
|
||||
use ::util::find_max_double;
|
||||
use crate::action::Action;
|
||||
use crate::actors;
|
||||
use crate::drawing;
|
||||
use crate::float_ord::FloatOrd;
|
||||
use crate::keyboard::KeyState;
|
||||
use crate::logging;
|
||||
use crate::popover;
|
||||
use crate::receiver;
|
||||
use crate::submission::{ Submission, SubmitData, Timestamp };
|
||||
use crate::util::find_max_double;
|
||||
|
||||
use ::imservice::ContentPurpose;
|
||||
use crate::imservice::ContentPurpose;
|
||||
|
||||
// Traits
|
||||
use std::borrow::Borrow;
|
||||
use ::logging::Warn;
|
||||
use crate::logging::Warn;
|
||||
|
||||
/// Gathers stuff defined in C or called by C
|
||||
pub mod c {
|
||||
use super::*;
|
||||
|
||||
use gtk_sys;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::receiver;
|
||||
use crate::submission::c::Submission as CSubmission;
|
||||
|
||||
use gtk_sys;
|
||||
use std::ops::{ Add, Sub };
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::util::CloneOwned;
|
||||
|
||||
// The following defined in C
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone)]
|
||||
@ -215,13 +220,17 @@ pub mod c {
|
||||
submission: CSubmission,
|
||||
widget_to_layout: Transformation,
|
||||
time: u32,
|
||||
manager: manager::c::Manager,
|
||||
popover: actors::popover::c::Actor,
|
||||
app_state: receiver::c::State,
|
||||
ui_keyboard: EekGtkKeyboard,
|
||||
) {
|
||||
let time = Timestamp(time);
|
||||
let layout = unsafe { &mut *layout };
|
||||
let submission = submission.clone_ref();
|
||||
let mut submission = submission.borrow_mut();
|
||||
let app_state = app_state.clone_owned();
|
||||
let popover_state = popover.clone_owned();
|
||||
|
||||
let ui_backend = UIBackend {
|
||||
widget_to_layout,
|
||||
keyboard: ui_keyboard,
|
||||
@ -236,7 +245,7 @@ pub mod c {
|
||||
&mut submission,
|
||||
Some(&ui_backend),
|
||||
time,
|
||||
Some(manager),
|
||||
Some((&popover_state, app_state.clone())),
|
||||
key,
|
||||
);
|
||||
}
|
||||
@ -315,13 +324,18 @@ pub mod c {
|
||||
x_widget: f64, y_widget: f64,
|
||||
widget_to_layout: Transformation,
|
||||
time: u32,
|
||||
manager: manager::c::Manager,
|
||||
popover: actors::popover::c::Actor,
|
||||
app_state: receiver::c::State,
|
||||
ui_keyboard: EekGtkKeyboard,
|
||||
) {
|
||||
let time = Timestamp(time);
|
||||
let layout = unsafe { &mut *layout };
|
||||
let submission = submission.clone_ref();
|
||||
let mut submission = submission.borrow_mut();
|
||||
// We only need to query state here, not update.
|
||||
// A copy is enough.
|
||||
let popover_state = popover.clone_owned();
|
||||
let app_state = app_state.clone_owned();
|
||||
let ui_backend = UIBackend {
|
||||
widget_to_layout,
|
||||
keyboard: ui_keyboard,
|
||||
@ -352,7 +366,7 @@ pub mod c {
|
||||
&mut submission,
|
||||
Some(&ui_backend),
|
||||
time,
|
||||
Some(manager),
|
||||
Some((&popover_state, app_state.clone())),
|
||||
key,
|
||||
);
|
||||
}
|
||||
@ -377,7 +391,7 @@ pub mod c {
|
||||
&mut submission,
|
||||
Some(&ui_backend),
|
||||
time,
|
||||
Some(manager),
|
||||
Some((&popover_state, app_state.clone())),
|
||||
key,
|
||||
);
|
||||
}
|
||||
@ -1035,7 +1049,11 @@ mod seat {
|
||||
submission: &mut Submission,
|
||||
ui: Option<&UIBackend>,
|
||||
time: Timestamp,
|
||||
manager: Option<manager::c::Manager>,
|
||||
// TODO: intermediate measure:
|
||||
// passing state conditionally because it's only used for popover.
|
||||
// Eventually, it should be used for sumitting button events,
|
||||
// and passed always.
|
||||
manager: Option<(&actors::popover::State, receiver::State)>,
|
||||
rckey: &Rc<RefCell<KeyState>>,
|
||||
) {
|
||||
let key: KeyState = {
|
||||
@ -1070,7 +1088,7 @@ mod seat {
|
||||
// only show when UI is present
|
||||
Action::ShowPreferences => if let Some(ui) = &ui {
|
||||
// only show when layout manager is available
|
||||
if let Some(manager) = manager {
|
||||
if let Some((manager, app_state)) = manager {
|
||||
let view = layout.get_current_view();
|
||||
let places = ::layout::procedures::find_key_places(
|
||||
view, &rckey,
|
||||
@ -1085,10 +1103,11 @@ mod seat {
|
||||
width: button.size.width,
|
||||
height: button.size.height,
|
||||
};
|
||||
::popover::show(
|
||||
popover::show(
|
||||
ui.keyboard,
|
||||
ui.widget_to_layout.reverse_bounds(bounds),
|
||||
manager,
|
||||
app_state,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ mod assert_matches;
|
||||
mod logging;
|
||||
|
||||
mod action;
|
||||
mod actors;
|
||||
mod animation;
|
||||
pub mod data;
|
||||
mod debug;
|
||||
@ -34,10 +35,10 @@ mod keyboard;
|
||||
mod layout;
|
||||
mod locale;
|
||||
mod main;
|
||||
mod manager;
|
||||
mod outputs;
|
||||
mod panel;
|
||||
mod popover;
|
||||
mod receiver;
|
||||
mod resources;
|
||||
mod state;
|
||||
mod style;
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include "eek/eek-types.h"
|
||||
#include "dbus.h"
|
||||
#include "panel.h"
|
||||
#include "src/popover.h"
|
||||
|
||||
|
||||
struct receiver;
|
||||
@ -23,9 +24,10 @@ struct rsobjects {
|
||||
struct squeek_state_manager *state_manager;
|
||||
struct submission *submission;
|
||||
struct squeek_wayland *wayland;
|
||||
struct squeek_popover *popover;
|
||||
};
|
||||
|
||||
void register_ui_loop_handler(struct receiver *receiver, struct panel_manager *panel, EekboardContextService *hint_manager, DBusHandler *dbus_handler);
|
||||
void register_ui_loop_handler(struct receiver *receiver, struct panel_manager *panel, struct squeek_popover *popover, EekboardContextService *hint_manager, DBusHandler *dbus_handler);
|
||||
|
||||
struct rsobjects squeek_init(void);
|
||||
|
||||
@ -33,3 +35,4 @@ void squeek_state_send_force_visible(struct squeek_state_manager *state);
|
||||
void squeek_state_send_force_hidden(struct squeek_state_manager *state);
|
||||
|
||||
void squeek_state_send_keyboard_present(struct squeek_state_manager *state, uint32_t keyboard_present);
|
||||
void squeek_state_send_layout_set(struct squeek_state_manager *state, char *name, char *layout, uint32_t timestamp);
|
||||
|
||||
53
src/main.rs
53
src/main.rs
@ -3,9 +3,11 @@
|
||||
*/
|
||||
|
||||
/*! Glue for the main loop. */
|
||||
use crate::panel;
|
||||
use crate::actors;
|
||||
use crate::animation;
|
||||
use crate::debug;
|
||||
use crate::state;
|
||||
use crate::data::loading;
|
||||
use crate::panel;
|
||||
use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver};
|
||||
|
||||
|
||||
@ -19,6 +21,7 @@ mod c {
|
||||
use crate::event_loop::driver;
|
||||
use crate::imservice::IMService;
|
||||
use crate::imservice::c::InputMethod;
|
||||
use crate::layout;
|
||||
use crate::outputs::Outputs;
|
||||
use crate::state;
|
||||
use crate::submission::Submission;
|
||||
@ -46,6 +49,7 @@ mod c {
|
||||
submission: Wrapped<Submission>,
|
||||
/// Not wrapped, because C needs to access this.
|
||||
wayland: *mut Wayland,
|
||||
popover: actors::popover::c::Actor,
|
||||
}
|
||||
|
||||
/// Corresponds to wayland.h::squeek_wayland.
|
||||
@ -78,7 +82,8 @@ mod c {
|
||||
extern "C" {
|
||||
#[allow(improper_ctypes)]
|
||||
fn init_wayland(wayland: *mut Wayland);
|
||||
fn eekboard_context_service_set_hint_purpose(service: HintManager, hint: u32, purpose: u32);
|
||||
#[allow(improper_ctypes)]
|
||||
fn eekboard_context_service_set_layout(service: HintManager, layout: *const layout::Layout, timestamp: u32);
|
||||
// This should probably only get called from the gtk main loop,
|
||||
// given that dbus handler is using glib.
|
||||
fn dbus_handler_set_visible(dbus: *const DBusHandler, visible: u8);
|
||||
@ -116,6 +121,7 @@ mod c {
|
||||
state_manager: Wrapped::new(state_manager),
|
||||
receiver: Wrapped::new(receiver),
|
||||
wayland: Box::into_raw(wayland),
|
||||
popover: Wrapped::new(actors::popover::State::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,6 +131,7 @@ mod c {
|
||||
fn register_ui_loop_handler(
|
||||
receiver: Wrapped<Receiver<Commands>>,
|
||||
panel_manager: panel::c::PanelManager,
|
||||
popover: actors::popover::c::Actor,
|
||||
hint_manager: HintManager,
|
||||
dbus_handler: *const DBusHandler,
|
||||
) {
|
||||
@ -137,7 +144,13 @@ mod c {
|
||||
receiver.attach(
|
||||
Some(&ctx),
|
||||
move |msg| {
|
||||
main_loop_handle_message(msg, panel_manager.clone(), hint_manager, dbus_handler);
|
||||
main_loop_handle_message(
|
||||
msg,
|
||||
panel_manager.clone(),
|
||||
&popover,
|
||||
hint_manager,
|
||||
dbus_handler,
|
||||
);
|
||||
Continue(true)
|
||||
},
|
||||
);
|
||||
@ -152,6 +165,7 @@ mod c {
|
||||
fn main_loop_handle_message(
|
||||
msg: Commands,
|
||||
panel_manager: Wrapped<panel::Manager>,
|
||||
popover: &actors::popover::c::Actor,
|
||||
hint_manager: HintManager,
|
||||
dbus_handler: *const DBusHandler,
|
||||
) {
|
||||
@ -164,24 +178,37 @@ mod c {
|
||||
unsafe { dbus_handler_set_visible(dbus_handler, visible as u8) };
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(hints) = msg.layout_hint_set {
|
||||
|
||||
if let Some(commands::SetLayout { description }) = msg.layout_selection {
|
||||
let animation::Contents {
|
||||
name,
|
||||
kind,
|
||||
overlay_name,
|
||||
purpose,
|
||||
} = description;
|
||||
actors::popover::set_overlay(popover, overlay_name.clone());
|
||||
let layout = loading::load_layout(name, kind, purpose, overlay_name);
|
||||
let layout = Box::into_raw(Box::new(layout));
|
||||
unsafe {
|
||||
eekboard_context_service_set_hint_purpose(
|
||||
hint_manager,
|
||||
hints.hint.bits(),
|
||||
hints.purpose.clone() as u32,
|
||||
)
|
||||
};
|
||||
eekboard_context_service_set_layout(hint_manager, layout, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod commands {
|
||||
use crate::animation;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SetLayout {
|
||||
pub description: animation::Contents,
|
||||
}
|
||||
}
|
||||
|
||||
/// The commands consumed by the main loop,
|
||||
/// to be sent out to external components.
|
||||
#[derive(Clone)]
|
||||
pub struct Commands {
|
||||
pub panel_visibility: Option<panel::Command>,
|
||||
pub layout_hint_set: Option<state::InputMethodDetails>,
|
||||
pub dbus_visible_set: Option<bool>,
|
||||
pub layout_selection: Option<commands::SetLayout>,
|
||||
}
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
/*! Procedures relating to the management of the switching of layouts */
|
||||
use ::util;
|
||||
|
||||
pub mod c {
|
||||
use std::os::raw::{c_char, c_void};
|
||||
|
||||
/// EekboardContextService*
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Manager(*const c_void);
|
||||
|
||||
extern "C" {
|
||||
pub fn eekboard_context_service_set_overlay(
|
||||
manager: Manager,
|
||||
name: *const c_char,
|
||||
);
|
||||
|
||||
pub fn eekboard_context_service_get_overlay(
|
||||
manager: Manager,
|
||||
) -> *const c_char;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the overlay name.
|
||||
/// The result lifetime is "as long as the C copy lives"
|
||||
pub fn get_overlay(manager: c::Manager) -> Option<String> {
|
||||
let raw_str = unsafe {
|
||||
c::eekboard_context_service_get_overlay(manager)
|
||||
};
|
||||
// this string is generated from Rust, should never be invalid
|
||||
util::c::as_str(&raw_str).unwrap()
|
||||
.map(String::from)
|
||||
}
|
||||
@ -52,7 +52,7 @@ make_widget (struct panel_manager *self)
|
||||
if (self->widget) {
|
||||
g_error("Widget already present");
|
||||
}
|
||||
self->widget = eek_gtk_keyboard_new (self->state, self->submission, self->layout);
|
||||
self->widget = eek_gtk_keyboard_new (self->state, self->submission, self->state_manager, self->popover);
|
||||
|
||||
gtk_widget_set_has_tooltip (self->widget, TRUE);
|
||||
gtk_container_add (GTK_CONTAINER(self->window), self->widget);
|
||||
@ -116,15 +116,16 @@ panel_manager_resize (struct panel_manager *self, uint32_t height)
|
||||
}
|
||||
|
||||
|
||||
struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout)
|
||||
struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_state_manager *state_manager, struct squeek_popover *popover)
|
||||
{
|
||||
struct panel_manager mgr = {
|
||||
.state = state,
|
||||
.submission = submission,
|
||||
.layout = layout,
|
||||
.window = NULL,
|
||||
.widget = NULL,
|
||||
.current_output = NULL,
|
||||
.state_manager = state_manager,
|
||||
.popover = popover,
|
||||
};
|
||||
return mgr;
|
||||
}
|
||||
|
||||
@ -2,14 +2,16 @@
|
||||
|
||||
#include "eek/layersurface.h"
|
||||
#include "src/layout.h"
|
||||
#include "src/main.h"
|
||||
#include "src/submission.h"
|
||||
|
||||
// Stores the objects that the panel and its widget will refer to
|
||||
struct panel_manager {
|
||||
EekboardContextService *state; // unowned
|
||||
/// Needed for instantiating the widget
|
||||
struct squeek_state_manager *state_manager; // shared reference
|
||||
struct squeek_popover *popover; // shared reference
|
||||
struct submission *submission; // unowned
|
||||
struct squeek_layout_state *layout;
|
||||
|
||||
PhoshLayerSurface *window;
|
||||
GtkWidget *widget; // nullable
|
||||
@ -18,4 +20,4 @@ struct panel_manager {
|
||||
struct wl_output *current_output;
|
||||
};
|
||||
|
||||
struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_layout_state *layout);
|
||||
struct panel_manager panel_manager_new(EekboardContextService *state, struct submission *submission, struct squeek_state_manager *state_manager, struct squeek_popover *popover);
|
||||
|
||||
5
src/popover.h
Normal file
5
src/popover.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
/// Popover state.
|
||||
/// Wrapped<actors::popover::State>
|
||||
struct squeek_popover;
|
||||
|
||||
@ -4,11 +4,13 @@ use gio;
|
||||
use gtk;
|
||||
use std::ffi::CString;
|
||||
use std::cmp::Ordering;
|
||||
use ::layout::c::{ Bounds, EekGtkKeyboard };
|
||||
use ::locale::{ OwnedTranslation, compare_current_locale };
|
||||
use ::logging;
|
||||
use ::manager;
|
||||
use ::resources;
|
||||
use crate::actors;
|
||||
use crate::layout::c::{ Bounds, EekGtkKeyboard };
|
||||
use crate::locale::{ OwnedTranslation, compare_current_locale };
|
||||
use crate::logging;
|
||||
use crate::receiver;
|
||||
use crate::resources;
|
||||
use crate::state;
|
||||
|
||||
// Traits
|
||||
use gio::prelude::ActionMapExt;
|
||||
@ -16,7 +18,7 @@ use gio::prelude::SettingsExt;
|
||||
use glib::translate::FromGlibPtrNone;
|
||||
use glib::variant::ToVariant;
|
||||
use gtk::prelude::*;
|
||||
use ::logging::Warn;
|
||||
use crate::logging::Warn;
|
||||
|
||||
mod c {
|
||||
use std::os::raw::c_char;
|
||||
@ -127,9 +129,11 @@ fn get_settings(schema_name: &str) -> Option<gio::Settings> {
|
||||
.map(|_sschema| gio::Settings::new(schema_name))
|
||||
}
|
||||
|
||||
fn set_layout(kind: String, name: String) {
|
||||
fn set_layout(kind: &str, name: &str) {
|
||||
let settings = get_settings("org.gnome.desktop.input-sources");
|
||||
if let Some(settings) = settings {
|
||||
let kind = String::from(kind);
|
||||
let name = String::from(name);
|
||||
#[cfg(feature = "glib_v0_14")]
|
||||
let inputs = settings.value("sources");
|
||||
#[cfg(not(feature = "glib_v0_14"))]
|
||||
@ -150,7 +154,7 @@ fn set_layout(kind: String, name: String) {
|
||||
|
||||
/// A reference to what the user wants to see
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
enum LayoutId {
|
||||
pub enum LayoutId {
|
||||
/// Affects the layout in system settings
|
||||
System {
|
||||
kind: String,
|
||||
@ -170,40 +174,23 @@ impl LayoutId {
|
||||
}
|
||||
|
||||
fn set_visible_layout(
|
||||
manager: manager::c::Manager,
|
||||
layout_id: LayoutId,
|
||||
layout_id: &LayoutId,
|
||||
) {
|
||||
match layout_id {
|
||||
LayoutId::System { kind, name } => {
|
||||
unsafe {
|
||||
use std::ptr;
|
||||
manager::c::eekboard_context_service_set_overlay(
|
||||
manager,
|
||||
ptr::null(),
|
||||
);
|
||||
}
|
||||
set_layout(kind, name);
|
||||
}
|
||||
LayoutId::Local(name) => {
|
||||
let name = CString::new(name.as_str()).unwrap();
|
||||
let name_ptr = name.as_ptr();
|
||||
unsafe {
|
||||
manager::c::eekboard_context_service_set_overlay(
|
||||
manager,
|
||||
name_ptr,
|
||||
)
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes into account first any overlays, then system layouts from the list
|
||||
fn get_current_layout(
|
||||
manager: manager::c::Manager,
|
||||
popover: &actors::popover::State,
|
||||
system_layouts: &Vec<LayoutId>,
|
||||
) -> Option<LayoutId> {
|
||||
match manager::get_overlay(manager) {
|
||||
Some(name) => Some(LayoutId::Local(name)),
|
||||
match &popover.overlay {
|
||||
Some(name) => Some(LayoutId::Local(name.into())),
|
||||
None => system_layouts.get(0).map(LayoutId::clone),
|
||||
}
|
||||
}
|
||||
@ -247,7 +234,8 @@ fn translate_layout_names(layouts: &Vec<LayoutId>) -> Vec<OwnedTranslation> {
|
||||
pub fn show(
|
||||
window: EekGtkKeyboard,
|
||||
position: Bounds,
|
||||
manager: manager::c::Manager,
|
||||
popover: &actors::popover::State,
|
||||
app_state: receiver::State,
|
||||
) {
|
||||
unsafe { gtk::set_initialized() };
|
||||
let window = unsafe { gtk::Widget::from_glib_none(window.0) };
|
||||
@ -327,7 +315,7 @@ pub fn show(
|
||||
|
||||
let action_group = gio::SimpleActionGroup::new();
|
||||
|
||||
if let Some(current_layout) = get_current_layout(manager, &system_layouts) {
|
||||
if let Some(current_layout) = get_current_layout(popover, &system_layouts) {
|
||||
let current_layout_name = all_layouts.iter()
|
||||
.find(
|
||||
|l| l.get_name() == current_layout.get_name()
|
||||
@ -356,10 +344,13 @@ pub fn show(
|
||||
.find(
|
||||
|choices| state == choices.get_name()
|
||||
).unwrap();
|
||||
set_visible_layout(
|
||||
manager,
|
||||
layout.clone(),
|
||||
)
|
||||
app_state
|
||||
.send(state::Event::OverlayChanged(layout.clone()))
|
||||
.or_print(
|
||||
logging::Problem::Bug,
|
||||
&format!("Can't send to state"),
|
||||
);
|
||||
set_visible_layout(layout)
|
||||
});
|
||||
},
|
||||
None => log_print!(
|
||||
|
||||
15
src/receiver.rs
Normal file
15
src/receiver.rs
Normal file
@ -0,0 +1,15 @@
|
||||
/*! Defines the application-wide message bus for updating state.*/
|
||||
|
||||
use crate::event_loop::driver::Threaded;
|
||||
|
||||
pub mod c {
|
||||
use super::*;
|
||||
use crate::util::c::Wrapped;
|
||||
pub type State = Wrapped<Threaded>;
|
||||
}
|
||||
|
||||
// The state receiver is an endpoint of a channel, so it's safely cloneable.
|
||||
// There's no need to keep it in a Rc.
|
||||
// The C version uses Wrapped with an underlying Rc,
|
||||
// because Wrapped is well-tested already.
|
||||
pub type State = Threaded;
|
||||
@ -56,8 +56,6 @@ struct squeekboard {
|
||||
/// Gsettings hook for visibility. TODO: this does not belong in gsettings.
|
||||
ServerContextService *settings_handler;
|
||||
struct panel_manager panel_manager; // Controls the shape of the panel.
|
||||
/// Currently wanted layout. TODO: merge into state::Application
|
||||
struct squeek_layout_state layout_choice;
|
||||
};
|
||||
|
||||
|
||||
@ -400,7 +398,7 @@ main (int argc, char **argv)
|
||||
// Also initializes wayland
|
||||
struct rsobjects rsobjects = squeek_init();
|
||||
|
||||
instance.settings_context = eekboard_context_service_new(&instance.layout_choice);
|
||||
instance.settings_context = eekboard_context_service_new(rsobjects.state_manager);
|
||||
|
||||
// set up dbus
|
||||
|
||||
@ -450,9 +448,10 @@ main (int argc, char **argv)
|
||||
|
||||
instance.panel_manager = panel_manager_new(instance.settings_context,
|
||||
rsobjects.submission,
|
||||
&instance.layout_choice);
|
||||
rsobjects.state_manager,
|
||||
rsobjects.popover);
|
||||
|
||||
register_ui_loop_handler(rsobjects.receiver, &instance.panel_manager, instance.settings_context, instance.dbus_handler);
|
||||
register_ui_loop_handler(rsobjects.receiver, &instance.panel_manager, rsobjects.popover, instance.settings_context, instance.dbus_handler);
|
||||
|
||||
session_register();
|
||||
|
||||
|
||||
259
src/state.rs
259
src/state.rs
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2021 Purism SPC
|
||||
/* Copyright (C) 2021,2022 Purism SPC
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
*/
|
||||
|
||||
@ -8,11 +8,14 @@
|
||||
use crate::animation;
|
||||
use crate::debug;
|
||||
use crate::imservice::{ ContentHint, ContentPurpose };
|
||||
use crate::layout::ArrangementKind;
|
||||
use crate::main;
|
||||
use crate::main::Commands;
|
||||
use crate::outputs;
|
||||
use crate::outputs::{Millimeter, OutputId, OutputState};
|
||||
use crate::panel;
|
||||
use crate::panel::PixelSize;
|
||||
use crate::popover;
|
||||
use crate::util::Rational;
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
@ -37,6 +40,29 @@ pub enum InputMethod {
|
||||
InactiveSince(Instant),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum LayoutSource {
|
||||
Xkb,
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl From<String> for LayoutSource {
|
||||
fn from(v: String) -> Self {
|
||||
if v.as_str() == "xkb" {
|
||||
LayoutSource::Xkb
|
||||
} else {
|
||||
LayoutSource::Other(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The user's preferred system layout
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LayoutChoice {
|
||||
pub name: String,
|
||||
pub source: LayoutSource,
|
||||
}
|
||||
|
||||
/// Incoming events.
|
||||
/// This contains events that cause a change to the internal state.
|
||||
#[derive(Clone, Debug)]
|
||||
@ -45,6 +71,8 @@ pub enum Event {
|
||||
Visibility(visibility::Event),
|
||||
PhysicalKeyboard(Presence),
|
||||
Output(outputs::Event),
|
||||
LayoutChoice(LayoutChoice),
|
||||
OverlayChanged(popover::LayoutId),
|
||||
Debug(debug::Event),
|
||||
/// Event triggered because a moment in time passed.
|
||||
/// Use to animate state transitions.
|
||||
@ -87,7 +115,7 @@ pub mod visibility {
|
||||
/// The outwardly visible state.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Outcome {
|
||||
pub visibility: animation::Outcome,
|
||||
pub panel: animation::Outcome,
|
||||
pub im: InputMethod,
|
||||
}
|
||||
|
||||
@ -98,36 +126,40 @@ impl Outcome {
|
||||
/// The receivers of the commands bear the burden
|
||||
/// of checking if the commands end up being no-ops.
|
||||
pub fn get_commands_to_reach(&self, new_state: &Self) -> Commands {
|
||||
let layout_hint_set = match new_state {
|
||||
Outcome {
|
||||
visibility: animation::Outcome::Visible{..},
|
||||
im: InputMethod::Active(hints),
|
||||
} => Some(hints.clone()),
|
||||
|
||||
Outcome {
|
||||
visibility: animation::Outcome::Visible{..},
|
||||
im: InputMethod::InactiveSince(_),
|
||||
} => Some(InputMethodDetails {
|
||||
hint: ContentHint::NONE,
|
||||
purpose: ContentPurpose::Normal,
|
||||
}),
|
||||
|
||||
Outcome {
|
||||
visibility: 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)),
|
||||
};
|
||||
|
||||
// Compare the old and new states as not to flood with updates,
|
||||
// which may look up in the file system.
|
||||
use animation::Outcome::*;
|
||||
let layout_selection = match &new_state.panel {
|
||||
Visible{ contents: new_contents, ..} => {
|
||||
let same
|
||||
= if let Visible { contents, .. } = &self.panel {
|
||||
contents == new_contents
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !same {
|
||||
Some(main::commands::SetLayout {
|
||||
description: new_contents.clone()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
animation::Outcome::Hidden => None,
|
||||
};
|
||||
|
||||
Commands {
|
||||
panel_visibility,
|
||||
layout_hint_set,
|
||||
dbus_visible_set,
|
||||
layout_selection,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,6 +188,13 @@ pub struct Application {
|
||||
/// but not sure about being allowed on non-touch displays.
|
||||
pub preferred_output: Option<OutputId>,
|
||||
pub outputs: HashMap<OutputId, OutputState>,
|
||||
/// We presume that the system always has some preference,
|
||||
/// even though we receive the preference after init,
|
||||
/// and we might not receive one at all (gsettings missing).
|
||||
/// Then a default is used.
|
||||
pub layout_choice: LayoutChoice,
|
||||
/// Manual override of the system layout
|
||||
pub overlay_layout: Option<popover::LayoutId>,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
@ -173,6 +212,11 @@ impl Application {
|
||||
debug_mode_enabled: false,
|
||||
preferred_output: None,
|
||||
outputs: Default::default(),
|
||||
layout_choice: LayoutChoice {
|
||||
name: String::from("us"),
|
||||
source: LayoutSource::Xkb,
|
||||
},
|
||||
overlay_layout: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,7 +301,18 @@ impl Application {
|
||||
im: InputMethod::InactiveSince(old),
|
||||
..self
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
Event::LayoutChoice(layout_choice) => Self {
|
||||
layout_choice,
|
||||
overlay_layout: None,
|
||||
..self
|
||||
},
|
||||
|
||||
Event::OverlayChanged(overlay_layout) => Self {
|
||||
overlay_layout: Some(overlay_layout),
|
||||
..self
|
||||
},
|
||||
};
|
||||
|
||||
if state.debug_mode_enabled {
|
||||
@ -273,7 +328,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.
|
||||
@ -301,61 +358,95 @@ 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>) {
|
||||
(
|
||||
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,
|
||||
purpose: match self.im {
|
||||
InputMethod::Active(InputMethodDetails { purpose, .. }) => purpose,
|
||||
InputMethod::InactiveSince(_) => ContentPurpose::Normal,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
match (self.physical_keyboard, self.visibility_override) {
|
||||
(_, visibility::State::ForcedHidden) => animation::Outcome::Hidden,
|
||||
(_, visibility::State::ForcedVisible) => visible,
|
||||
@ -446,7 +537,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),
|
||||
@ -455,7 +546,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
|
||||
@ -472,7 +566,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),
|
||||
@ -502,7 +596,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),
|
||||
@ -515,7 +609,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),
|
||||
@ -537,7 +631,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),
|
||||
@ -550,7 +644,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),
|
||||
@ -571,7 +665,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),
|
||||
@ -583,7 +677,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),
|
||||
@ -593,7 +687,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),
|
||||
@ -605,7 +699,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,
|
||||
@ -619,10 +713,13 @@ pub mod test {
|
||||
}),
|
||||
scale: 2,
|
||||
}),
|
||||
Some(PixelSize {
|
||||
scale_factor: 2,
|
||||
pixels: 420,
|
||||
}),
|
||||
Some((
|
||||
PixelSize {
|
||||
scale_factor: 2,
|
||||
pixels: 420,
|
||||
},
|
||||
ArrangementKind::Base,
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user