mirror of
https://github.com/danbulant/cushy
synced 2026-07-06 19:50:40 +00:00
parent
8d3a4a42cf
commit
470267ccfe
2 changed files with 58 additions and 61 deletions
|
|
@ -27,14 +27,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
Using a `Resize` widget in the root hierarchy allows setting minimum width and
|
Using a `Resize` widget in the root hierarchy allows setting minimum width and
|
||||||
heights for the content.
|
heights for the content.
|
||||||
|
|
||||||
[92]: https://github.com/khonsulabs/gooey/issues/92
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- A memory leak has been fixed that prevented the underlying widget tree of each
|
- A memory leak has been fixed that prevented the underlying widget tree of each
|
||||||
window from being dropped. This was caused by a reference counting cycle, and
|
window from being dropped. This was caused by a reference counting cycle, and
|
||||||
has been fixed by switching `MountedWidget` to use a weak reference internally
|
has been fixed by switching `MountedWidget` to use a weak reference internally
|
||||||
and having the window hold the strong reference to the tree.
|
and having the window hold the strong reference to the tree.
|
||||||
|
- [#113][113]: `Input` now constraints its internal selection to the value's
|
||||||
|
length automatically. This fixes an issue where the backspace key no longer
|
||||||
|
would work after clearing the text field by setting the `Dynamic`.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
|
@ -59,6 +60,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- `Window::on_close` sets a callback to be invoked when the window has closed.
|
- `Window::on_close` sets a callback to be invoked when the window has closed.
|
||||||
|
|
||||||
[91]: https://github.com/khonsulabs/gooey/issues/91
|
[91]: https://github.com/khonsulabs/gooey/issues/91
|
||||||
|
[92]: https://github.com/khonsulabs/gooey/issues/92
|
||||||
|
[113]: https://github.com/khonsulabs/gooey/issues/113
|
||||||
|
|
||||||
## v0.1.3 (2023-12-19)
|
## v0.1.3 (2023-12-19)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,35 +50,23 @@ pub struct Input<Storage> {
|
||||||
window_focused: bool,
|
window_focused: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CachedLayout {
|
#[derive(Eq, PartialEq, Clone, Copy)]
|
||||||
bytes: usize,
|
struct CacheKey {
|
||||||
color: Color,
|
|
||||||
generation: Generation,
|
generation: Generation,
|
||||||
mask_generation: Option<Generation>,
|
mask_generation: Option<Generation>,
|
||||||
placeholder_generation: Option<Generation>,
|
placeholder_generation: Option<Generation>,
|
||||||
mask_bytes: usize,
|
|
||||||
width: Option<Px>,
|
width: Option<Px>,
|
||||||
measured: MeasuredText<Px>,
|
color: Color,
|
||||||
placeholder: MeasuredText<Px>,
|
mask_bytes: usize,
|
||||||
|
cursor: Cursor,
|
||||||
|
selection: Option<Cursor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CachedLayout {
|
struct CachedLayout {
|
||||||
pub fn is_current(
|
bytes: usize,
|
||||||
&self,
|
measured: MeasuredText<Px>,
|
||||||
generation: Generation,
|
placeholder: MeasuredText<Px>,
|
||||||
mask_generation: Option<Generation>,
|
key: CacheKey,
|
||||||
placeholder_generation: Option<Generation>,
|
|
||||||
width: Option<Px>,
|
|
||||||
color: Color,
|
|
||||||
mask_bytes: usize,
|
|
||||||
) -> bool {
|
|
||||||
self.generation == generation
|
|
||||||
&& self.mask_generation == mask_generation
|
|
||||||
&& self.placeholder_generation == placeholder_generation
|
|
||||||
&& self.width == width
|
|
||||||
&& self.color == color
|
|
||||||
&& self.mask_bytes == mask_bytes
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current selection of an [`Input`].
|
/// The current selection of an [`Input`].
|
||||||
|
|
@ -223,7 +211,8 @@ where
|
||||||
} else if cursor.offset > 0 {
|
} else if cursor.offset > 0 {
|
||||||
let mut value = self.value.lock();
|
let mut value = self.value.lock();
|
||||||
let length = value.as_str().len();
|
let length = value.as_str().len();
|
||||||
if length == 0 || cursor.offset == 0 || cursor.offset > length {
|
|
||||||
|
if length == 0 || cursor.offset == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -379,7 +368,16 @@ where
|
||||||
self.selection.cursor = self.cursor_from_point(position, context);
|
self.selection.cursor = self.cursor_from_point(position, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn constrain_selection(&mut self) {
|
||||||
|
let length = self.value.map_ref(|s| s.as_str().len());
|
||||||
|
self.selection.cursor.offset = self.selection.cursor.offset.min(length);
|
||||||
|
if let Some(start) = &mut self.selection.start {
|
||||||
|
start.offset = start.offset.min(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn selected_range(&mut self) -> (Cursor, Option<Cursor>) {
|
fn selected_range(&mut self) -> (Cursor, Option<Cursor>) {
|
||||||
|
self.constrain_selection();
|
||||||
match self.selection.start {
|
match self.selection.start {
|
||||||
Some(start) => match start.offset.cmp(&self.selection.cursor.offset) {
|
Some(start) => match start.offset.cmp(&self.selection.cursor.offset) {
|
||||||
Ordering::Less => (start, Some(self.selection.cursor)),
|
Ordering::Less => (start, Some(self.selection.cursor)),
|
||||||
|
|
@ -556,25 +554,25 @@ where
|
||||||
width: Option<Px>,
|
width: Option<Px>,
|
||||||
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
|
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
|
||||||
) -> CacheInfo<'_> {
|
) -> CacheInfo<'_> {
|
||||||
let (mut cursor, mut selection) = self.selected_range();
|
|
||||||
let generation = self.value.generation();
|
|
||||||
let mask_generation = self.mask_symbol.generation();
|
|
||||||
let placeholder_generation = self.placeholder.generation();
|
|
||||||
let mut mask_bytes = self
|
|
||||||
.mask_symbol
|
|
||||||
.map(|sym| sym.graphemes(true).next().map_or(0, str::len));
|
|
||||||
let color = context.get(&TextColor);
|
|
||||||
context.invalidate_when_changed(&self.value);
|
context.invalidate_when_changed(&self.value);
|
||||||
|
|
||||||
|
let mut key = {
|
||||||
|
let (cursor, selection) = self.selected_range();
|
||||||
|
CacheKey {
|
||||||
|
generation: self.value.generation(),
|
||||||
|
mask_generation: self.mask_symbol.generation(),
|
||||||
|
placeholder_generation: self.placeholder.generation(),
|
||||||
|
width,
|
||||||
|
color: context.get(&TextColor),
|
||||||
|
mask_bytes: self
|
||||||
|
.mask_symbol
|
||||||
|
.map(|sym| sym.graphemes(true).next().map_or(0, str::len)),
|
||||||
|
cursor,
|
||||||
|
selection,
|
||||||
|
}
|
||||||
|
};
|
||||||
match &mut self.cache {
|
match &mut self.cache {
|
||||||
Some(cache)
|
Some(cache) if cache.key == key => {}
|
||||||
if cache.is_current(
|
|
||||||
generation,
|
|
||||||
mask_generation,
|
|
||||||
placeholder_generation,
|
|
||||||
width,
|
|
||||||
color,
|
|
||||||
mask_bytes,
|
|
||||||
) => {}
|
|
||||||
_ => {
|
_ => {
|
||||||
let (bytes, measured, placeholder, ) = self.value.map_ref(|storage| {
|
let (bytes, measured, placeholder, ) = self.value.map_ref(|storage| {
|
||||||
let mut text = storage.as_str();
|
let mut text = storage.as_str();
|
||||||
|
|
@ -591,9 +589,9 @@ where
|
||||||
// Technically something more optimal than asking the
|
// Technically something more optimal than asking the
|
||||||
// layout system to lay out a repeated string should be
|
// layout system to lay out a repeated string should be
|
||||||
// doable, but it seems like a lot of code.
|
// doable, but it seems like a lot of code.
|
||||||
mask_bytes = first_grapheme.len();
|
key.mask_bytes = first_grapheme.len();
|
||||||
let char_count = text.graphemes(true).count();
|
let char_count = text.graphemes(true).count();
|
||||||
bytes = mask_bytes * char_count;
|
bytes = key.mask_bytes * char_count;
|
||||||
self.mask.truncate(bytes);
|
self.mask.truncate(bytes);
|
||||||
|
|
||||||
while self.mask.len() < bytes {
|
while self.mask.len() < bytes {
|
||||||
|
|
@ -601,12 +599,12 @@ where
|
||||||
}
|
}
|
||||||
text = &self.mask;
|
text = &self.mask;
|
||||||
} else {
|
} else {
|
||||||
mask_bytes = 0;
|
key.mask_bytes = 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
context.apply_current_font_settings();
|
context.apply_current_font_settings();
|
||||||
let mut text = Text::new(text, color);
|
let mut text = Text::new(text, key.color);
|
||||||
if let Some(width) = width {
|
if let Some(width) = width {
|
||||||
text = text.wrap_at(width);
|
text = text.wrap_at(width);
|
||||||
}
|
}
|
||||||
|
|
@ -617,29 +615,25 @@ where
|
||||||
});
|
});
|
||||||
self.cache = Some(CachedLayout {
|
self.cache = Some(CachedLayout {
|
||||||
bytes,
|
bytes,
|
||||||
color,
|
|
||||||
generation,
|
|
||||||
mask_generation,
|
|
||||||
placeholder_generation,
|
|
||||||
mask_bytes,
|
|
||||||
width,
|
|
||||||
measured,
|
measured,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
key,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust the selection cursors to accommodate the difference in unicode
|
// Adjust the selection cursors to accommodate the difference in unicode
|
||||||
// widths of characters in the source string and the mask_char.
|
// widths of characters in the source string and the mask_char.
|
||||||
if mask_bytes > 0 {
|
if key.mask_bytes > 0 {
|
||||||
self.value.map_ref(|value| {
|
self.value.map_ref(|value| {
|
||||||
let value = value.as_str();
|
let value = value.as_str();
|
||||||
assert!(cursor.offset <= value.len());
|
assert!(key.cursor.offset <= value.len());
|
||||||
cursor.offset = value[..cursor.offset].graphemes(true).count() * mask_bytes;
|
key.cursor.offset =
|
||||||
if let Some(selection) = &mut selection {
|
value[..key.cursor.offset].graphemes(true).count() * key.mask_bytes;
|
||||||
|
if let Some(selection) = &mut key.selection {
|
||||||
assert!(selection.offset <= value.len());
|
assert!(selection.offset <= value.len());
|
||||||
selection.offset =
|
selection.offset =
|
||||||
value[..selection.offset].graphemes(true).count() * mask_bytes;
|
value[..selection.offset].graphemes(true).count() * key.mask_bytes;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -649,9 +643,9 @@ where
|
||||||
measured: &cache.measured,
|
measured: &cache.measured,
|
||||||
placeholder: &cache.placeholder,
|
placeholder: &cache.placeholder,
|
||||||
bytes: cache.bytes,
|
bytes: cache.bytes,
|
||||||
masked: mask_bytes > 0,
|
masked: key.mask_bytes > 0,
|
||||||
cursor,
|
cursor: key.cursor,
|
||||||
selection,
|
selection: key.selection,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue