mirror of
https://github.com/danbulant/cushy
synced 2026-05-24 20:32:28 +00:00
Added blinking cursor
This commit is contained in:
parent
dc6c22372b
commit
69f6f68ba6
2 changed files with 100 additions and 23 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
|
@ -35,13 +35,14 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.3"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
||||
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -95,7 +96,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "appit"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/khonsulabs/appit#665107f59b0d1b1cc6780800fa1e172faf615f07"
|
||||
source = "git+https://github.com/khonsulabs/appit#91c540c2a2db69eb25ea47eccb7aac1eb911933e"
|
||||
dependencies = [
|
||||
"raw-window-handle 0.5.2",
|
||||
"winit",
|
||||
|
|
@ -1607,9 +1608,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.3"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
|
|
@ -1881,9 +1882,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "web-time"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8208e3fdbc243c8fd30805721869242a7f6de3e2e9f3b057652ab36e52ae1e87"
|
||||
checksum = "57099a701fb3a8043f993e8228dc24229c7b942e2b009a1b962e54489ba1d3bf"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
|
@ -2313,3 +2314,23 @@ name = "zeno"
|
|||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81ba595b9f2772fbee2312de30eeb80ec773b4cb2f1e8098db024afadda6c06f"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "772666c41fb6dceaf520b564b962d738a8e1a83b41bd48945f50837aed78bb1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::fmt::Debug;
|
||||
use std::time::Duration;
|
||||
|
||||
use kludgine::app::winit::event::Ime;
|
||||
use kludgine::app::winit::keyboard::Key;
|
||||
|
|
@ -17,10 +18,13 @@ use crate::styles::{HighlightColor, LineHeight, Styles, TextColor, TextSize};
|
|||
use crate::utils::ModifiersExt;
|
||||
use crate::widget::{EventHandling, IntoValue, Value, Widget, HANDLED, UNHANDLED};
|
||||
|
||||
const CURSOR_BLINK_DURATION: Duration = Duration::from_millis(500);
|
||||
|
||||
#[must_use]
|
||||
pub struct Input {
|
||||
pub text: Value<String>,
|
||||
editor: Option<LiveEditor>,
|
||||
cursor_state: CursorState,
|
||||
}
|
||||
|
||||
impl Input {
|
||||
|
|
@ -32,6 +36,7 @@ impl Input {
|
|||
Self {
|
||||
text: initial_text.into_value(),
|
||||
editor: None,
|
||||
cursor_state: CursorState::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,11 +126,14 @@ impl Widget for Input {
|
|||
y: location.y.0,
|
||||
},
|
||||
);
|
||||
self.cursor_state.force_on();
|
||||
context.set_needs_redraw();
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) {
|
||||
self.cursor_state.update(context.elapsed());
|
||||
let cursor_state = self.cursor_state;
|
||||
let size = context.graphics.size();
|
||||
let styles = context.query_style(&[&TextColor, &HighlightColor]);
|
||||
let highlight = styles.get_or_default(&HighlightColor);
|
||||
|
|
@ -246,18 +254,21 @@ impl Widget for Input {
|
|||
(Err(_), Err(_)) => {}
|
||||
}
|
||||
} else if let Ok((location, _)) = cursor_glyph(buffer, &cursor) {
|
||||
context.graphics.draw_shape(
|
||||
&Shape::filled_rect(
|
||||
Rect::new(
|
||||
location,
|
||||
Size::new(Px(1), line_height),
|
||||
if cursor_state.visible {
|
||||
context.graphics.draw_shape(
|
||||
&Shape::filled_rect(
|
||||
Rect::new(
|
||||
location,
|
||||
Size::new(Px(1), line_height),
|
||||
),
|
||||
highlight, // TODO cursor should be a bold color, highlight probably not. This should have its own color.
|
||||
),
|
||||
highlight, // TODO cursor should be a bold color, highlight probably not. This should have its own color.
|
||||
),
|
||||
Point::default(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
Point::default(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
context.redraw_in(cursor_state.remaining_until_blink);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -309,7 +320,7 @@ impl Widget for Input {
|
|||
"Keyboard input: {:?}. {:?}, {:?}",
|
||||
input.logical_key, input.text, input.physical_key
|
||||
);
|
||||
match (input.logical_key, input.text) {
|
||||
let handled = match (input.logical_key, input.text) {
|
||||
(key @ (Key::Backspace | Key::Delete), _) => {
|
||||
editor.action(
|
||||
context.kludgine.font_system(),
|
||||
|
|
@ -319,7 +330,6 @@ impl Widget for Input {
|
|||
_ => unreachable!("previously matched"),
|
||||
},
|
||||
);
|
||||
context.set_needs_redraw();
|
||||
HANDLED
|
||||
}
|
||||
(key @ (Key::ArrowLeft | Key::ArrowDown | Key::ArrowUp | Key::ArrowRight), _) => {
|
||||
|
|
@ -346,16 +356,21 @@ impl Widget for Input {
|
|||
_ => unreachable!("previously matched"),
|
||||
},
|
||||
);
|
||||
context.set_needs_redraw();
|
||||
HANDLED
|
||||
}
|
||||
(_, Some(text)) if !context.modifiers().state().primary() => {
|
||||
editor.insert_string(&text, None);
|
||||
context.set_needs_redraw();
|
||||
HANDLED
|
||||
}
|
||||
(_, _) => UNHANDLED,
|
||||
};
|
||||
|
||||
if handled.is_break() {
|
||||
context.set_needs_redraw();
|
||||
self.cursor_state.force_on();
|
||||
}
|
||||
|
||||
handled
|
||||
}
|
||||
|
||||
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_, '_>) -> EventHandling {
|
||||
|
|
@ -379,7 +394,6 @@ impl Widget for Input {
|
|||
}
|
||||
|
||||
fn blur(&mut self, context: &mut EventContext<'_, '_>) {
|
||||
println!("Blur");
|
||||
context.set_ime_allowed(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -459,3 +473,45 @@ enum NotVisible {
|
|||
Before,
|
||||
After,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct CursorState {
|
||||
visible: bool,
|
||||
remaining_until_blink: Duration,
|
||||
}
|
||||
|
||||
impl Default for CursorState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
visible: true,
|
||||
remaining_until_blink: CURSOR_BLINK_DURATION,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CursorState {
|
||||
pub fn update(&mut self, elapsed: Duration) {
|
||||
let total_cycles = elapsed.as_nanos() / CURSOR_BLINK_DURATION.as_nanos();
|
||||
let remaining = Duration::from_nanos(
|
||||
u64::try_from(elapsed.as_nanos() % CURSOR_BLINK_DURATION.as_nanos())
|
||||
.expect("remainder fits in u64"),
|
||||
);
|
||||
// If we have an odd number of totaal cycles, flip the visibility.
|
||||
if total_cycles & 1 == 1 {
|
||||
self.visible = !self.visible;
|
||||
}
|
||||
|
||||
if let Some(remaining) = self.remaining_until_blink.checked_sub(remaining) {
|
||||
self.remaining_until_blink = remaining;
|
||||
} else {
|
||||
self.visible = !self.visible;
|
||||
self.remaining_until_blink =
|
||||
CURSOR_BLINK_DURATION - (remaining - self.remaining_until_blink);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn force_on(&mut self) {
|
||||
self.visible = true;
|
||||
self.remaining_until_blink = CURSOR_BLINK_DURATION;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue