chrome: add virtual keyboard ui

This commit is contained in:
William Casarin
2025-08-06 18:53:45 -07:00
parent 87cb5ed515
commit c60e1af3eb
6 changed files with 95 additions and 32 deletions

1
Cargo.lock generated
View File

@@ -3532,6 +3532,7 @@ dependencies = [
name = "notedeck_chrome" name = "notedeck_chrome"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"bitflags 2.9.1",
"eframe", "eframe",
"egui", "egui",
"egui-winit", "egui-winit",

View File

@@ -1,12 +1,22 @@
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
pub mod android; pub mod android;
const VIRT_HEIGHT: i32 = 400;
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
pub fn virtual_keyboard_height() -> i32 { pub fn virtual_keyboard_height(virt: bool) -> i32 {
if virt {
VIRT_HEIGHT
} else {
android::virtual_keyboard_height() android::virtual_keyboard_height()
} }
}
#[cfg(not(target_os = "android"))] #[cfg(not(target_os = "android"))]
pub fn virtual_keyboard_height() -> i32 { pub fn virtual_keyboard_height(virt: bool) -> i32 {
if virt {
VIRT_HEIGHT
} else {
0 0
} }
}

View File

@@ -9,6 +9,7 @@ license = "GPLv3"
description = "The nostr browser" description = "The nostr browser"
[dependencies] [dependencies]
bitflags = { workspace = true }
eframe = { workspace = true } eframe = { workspace = true }
egui_tabs = { workspace = true } egui_tabs = { workspace = true }
egui_extras = { workspace = true } egui_extras = { workspace = true }

View File

@@ -2,6 +2,7 @@
//#[cfg(target_arch = "wasm32")] //#[cfg(target_arch = "wasm32")]
//use wasm_bindgen::prelude::*; //use wasm_bindgen::prelude::*;
use crate::app::NotedeckApp; use crate::app::NotedeckApp;
use crate::ChromeOptions;
use eframe::CreationContext; use eframe::CreationContext;
use egui::{vec2, Button, Color32, Label, Layout, Rect, RichText, ThemePreference, Widget}; use egui::{vec2, Button, Color32, Label, Layout, Rect, RichText, ThemePreference, Widget};
use egui_extras::{Size, StripBuilder}; use egui_extras::{Size, StripBuilder};
@@ -24,33 +25,13 @@ use std::collections::HashMap;
static ICON_WIDTH: f32 = 40.0; static ICON_WIDTH: f32 = 40.0;
pub static ICON_EXPANSION_MULTIPLE: f32 = 1.2; pub static ICON_EXPANSION_MULTIPLE: f32 = 1.2;
#[derive(Default)]
pub struct Chrome { pub struct Chrome {
active: i32, active: i32,
open: bool,
tab_selected: i32, tab_selected: i32,
options: ChromeOptions,
apps: Vec<NotedeckApp>, apps: Vec<NotedeckApp>,
pub repaint_causes: HashMap<egui::RepaintCause, u64>, pub repaint_causes: HashMap<egui::RepaintCause, u64>,
#[cfg(feature = "memory")]
show_memory_debug: bool,
show_repaint_debug: bool,
}
impl Default for Chrome {
fn default() -> Self {
Self {
active: 0,
repaint_causes: Default::default(),
tab_selected: 0,
// sidemenu is not open by default on mobile/narrow uis
open: !notedeck::ui::is_compiled_as_mobile(),
apps: vec![],
#[cfg(feature = "memory")]
show_memory_debug: false,
show_repaint_debug: false,
}
}
} }
/// When you click the toolbar button, these actions /// When you click the toolbar button, these actions
@@ -223,7 +204,7 @@ impl Chrome {
} }
pub fn toggle(&mut self) { pub fn toggle(&mut self) {
self.open = !self.open; self.options.toggle(ChromeOptions::IsOpen);
} }
pub fn add_app(&mut self, app: NotedeckApp) { pub fn add_app(&mut self, app: NotedeckApp) {
@@ -366,7 +347,9 @@ impl Chrome {
fn amount_open(&self, ui: &mut egui::Ui) -> f32 { fn amount_open(&self, ui: &mut egui::Ui) -> f32 {
let open_id = egui::Id::new("chrome_open"); let open_id = egui::Id::new("chrome_open");
let side_panel_width: f32 = 74.0; let side_panel_width: f32 = 74.0;
ui.ctx().animate_bool(open_id, self.open) * side_panel_width ui.ctx()
.animate_bool(open_id, self.options.contains(ChromeOptions::IsOpen))
* side_panel_width
} }
fn toolbar_height() -> f32 { fn toolbar_height() -> f32 {
@@ -477,12 +460,25 @@ impl Chrome {
fn show(&mut self, ctx: &mut AppContext, ui: &mut egui::Ui) -> Option<ChromePanelAction> { fn show(&mut self, ctx: &mut AppContext, ui: &mut egui::Ui) -> Option<ChromePanelAction> {
ui.spacing_mut().item_spacing.x = 0.0; ui.spacing_mut().item_spacing.x = 0.0;
if notedeck::ui::is_narrow(ui.ctx()) { if ctx.args.options.contains(NotedeckOptions::Debug)
&& ui.ctx().input(|i| i.key_pressed(egui::Key::Backtick))
{
self.options.toggle(ChromeOptions::VirtualKeyboard);
}
let r = if notedeck::ui::is_narrow(ui.ctx()) {
self.toolbar_chrome(ctx, ui) self.toolbar_chrome(ctx, ui)
} else { } else {
let amt_open = self.amount_open(ui); let amt_open = self.amount_open(ui);
self.panel(ctx, StripBuilder::new(ui), amt_open) self.panel(ctx, StripBuilder::new(ui), amt_open)
};
// virtual keyboard
if self.options.contains(ChromeOptions::VirtualKeyboard) {
virtual_keyboard_ui(ui);
} }
r
} }
fn topdown_sidebar(&mut self, ui: &mut egui::Ui, i18n: &mut Localization) { fn topdown_sidebar(&mut self, ui: &mut egui::Ui, i18n: &mut Localization) {
@@ -497,7 +493,7 @@ impl Chrome {
if ui.add(expand_side_panel_button()).clicked() { if ui.add(expand_side_panel_button()).clicked() {
//self.active = (self.active + 1) % (self.apps.len() as i32); //self.active = (self.active + 1) % (self.apps.len() as i32);
self.open = !self.open; self.options.toggle(ChromeOptions::IsOpen);
} }
ui.add_space(4.0); ui.add_space(4.0);
@@ -1023,10 +1019,10 @@ fn bottomup_sidebar(
.on_hover_cursor(egui::CursorIcon::PointingHand); .on_hover_cursor(egui::CursorIcon::PointingHand);
if r.clicked() { if r.clicked() {
chrome.show_repaint_debug = !chrome.show_repaint_debug; chrome.options.toggle(ChromeOptions::RepaintDebug);
} }
if chrome.show_repaint_debug { if chrome.options.contains(ChromeOptions::RepaintDebug) {
for cause in ui.ctx().repaint_causes() { for cause in ui.ctx().repaint_causes() {
chrome chrome
.repaint_causes .repaint_causes
@@ -1218,3 +1214,21 @@ fn repaint_causes_window(ui: &mut egui::Ui, causes: &HashMap<egui::RepaintCause,
}); });
}); });
} }
fn virtual_keyboard_ui(ui: &mut egui::Ui) {
let height = notedeck::platform::virtual_keyboard_height(true);
let screen_rect = ui.ctx().screen_rect();
let min = egui::Pos2::new(0.0, screen_rect.max.y - height as f32);
let rect = Rect::from_min_max(min, screen_rect.max);
let painter = ui.painter_at(rect);
painter.rect_filled(rect, 0.0, Color32::from_black_alpha(200));
ui.put(rect, |ui: &mut egui::Ui| {
ui.centered_and_justified(|ui| {
ui.label("This is a keyboard");
})
.response
});
}

View File

@@ -5,6 +5,8 @@ mod android;
mod app; mod app;
mod chrome; mod chrome;
mod options;
pub use app::NotedeckApp; pub use app::NotedeckApp;
pub use chrome::Chrome; pub use chrome::Chrome;
pub use options::ChromeOptions;

View File

@@ -0,0 +1,35 @@
use bitflags::bitflags;
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ChromeOptions: u64 {
/// Is the chrome currently open?
const NoOptions = 0;
/// Is the chrome currently open?
const IsOpen = 1 << 0;
/// Are we simulating a virtual keyboard? This is mostly for debugging
/// if we are too lazy to open up a real mobile device with soft
/// keyboard
const VirtualKeyboard = 1 << 1;
/// Are we showing the memory debug window?
const MemoryDebug = 1 << 2;
/// Repaint debug
const RepaintDebug = 1 << 3;
}
}
impl Default for ChromeOptions {
fn default() -> Self {
let mut options = ChromeOptions::NoOptions;
options.set(
ChromeOptions::IsOpen,
!notedeck::ui::is_compiled_as_mobile(),
);
options
}
}