Skip to content

Commit

Permalink
Changelog and cleanup
Browse files Browse the repository at this point in the history
- Make invalidate_text_field not take an Option
- Use WidgetWrapper for Padding
- remove some unused code
- Other small doc fixes
- Add TextBox example to web examples
  • Loading branch information
cmyr committed Mar 16, 2021
1 parent a598f3e commit 98ece99
Show file tree
Hide file tree
Showing 9 changed files with 36 additions and 57 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ You can find its changes [documented below](#070---2021-01-01).
# Unreleased

### Highlights
- International text input support (IME) on macOS.

### Added
- Add `scroll()` method in WidgetExt ([#1600] by [@totsteps])
Expand All @@ -22,6 +23,7 @@ You can find its changes [documented below](#070---2021-01-01).
- Shell: windows implementation from content_insets ([#1592] by [@HoNile])
- Shell: IME API and macOS IME implementation ([#1619] by [@lord])
- Scroll::content_must_fill and a few other new Scroll methods ([#1635] by [@cmyr])
- New `TextBox` widget with IME integration ([#1636] by [@cmyr])
- Added ListIter implementations for OrdMap ([#1641] by [@Lejero])

### Changed
Expand Down Expand Up @@ -635,6 +637,7 @@ Last release without a changelog :(
[#1619]: https://github.com/linebender/druid/pull/1619
[#1634]: https://github.com/linebender/druid/pull/1634
[#1635]: https://github.com/linebender/druid/pull/1635
[#1636]: https://github.com/linebender/druid/pull/1636
[#1641]: https://github.com/linebender/druid/pull/1641
[#1647]: https://github.com/linebender/druid/pull/1647

Expand Down
1 change: 1 addition & 0 deletions druid/examples/web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ impl_example!(styled_text.unwrap());
impl_example!(switches);
impl_example!(timer);
impl_example!(tabs);
impl_example!(textbox);
impl_example!(transparency);
impl_example!(view_switcher);
impl_example!(widget_gallery);
Expand Down
5 changes: 3 additions & 2 deletions druid/src/contexts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use tracing::{error, trace, warn};
use crate::core::{CommandQueue, CursorChange, FocusChange, WidgetState};
use crate::env::KeyLike;
use crate::piet::{Piet, PietText, RenderContext};
use crate::shell::text::Event as ImeInvalidation;
use crate::shell::Region;
use crate::text::{ImeHandlerRef, TextFieldRegistration};
use crate::{
Expand Down Expand Up @@ -389,10 +390,10 @@ impl_context_method!(EventCtx<'_, '_>, UpdateCtx<'_, '_>, LifeCycleCtx<'_, '_>,
/// A widget that accepts text input should call this anytime input state
/// (such as the text or the selection) changes as a result of a non text-input
/// event.
pub fn invalidate_text_input(&mut self, event: Option<crate::shell::text::Event>) {
pub fn invalidate_text_input(&mut self, event: ImeInvalidation) {
let payload = commands::ImeInvalidation {
widget: self.widget_id(),
event: event.unwrap_or(crate::shell::text::Event::Reset),
event,
};
let cmd = commands::INVALIDATE_IME
.with(payload)
Expand Down
14 changes: 9 additions & 5 deletions druid/src/text/input_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ pub struct TextComponent<T> {
lock: Arc<Cell<ImeLock>>,
}

/// The inner state of an `EditSession`.
/// Editable text state.
///
/// This may be modified directly, or it may be modified by the platform.
/// This is the inner state of a [`TextComponent`]. It should only be accessed
/// through its containing [`TextComponent`], or by the platform through an
/// [`ImeHandlerRef`] created by [`TextComponent::input_handler`].
#[derive(Debug, Clone)]
pub struct EditSession<T> {
/// The inner [`TextLayout`] object.
Expand Down Expand Up @@ -149,8 +151,10 @@ impl<T: TextStorage + EditableText> ImeHandlerRef for EditSessionRef<T> {
}

impl TextComponent<()> {
/// If the payload is true, this follows an edit, and the view will need to be laid
/// out before scrolling.
/// A notification sent by the component when the cursor has moved.
///
/// If the payload is true, this follows an edit, and the view will need
/// layout before scrolling.
pub const SCROLL_TO: Selector<bool> = Selector::new("druid-builtin.textbox-scroll-to");

/// A notification sent by the component when the user hits return.
Expand Down Expand Up @@ -334,7 +338,7 @@ impl<T: TextStorage + EditableText> Widget<T> for TextComponent<T> {
let new_origin = ctx.window_origin();
if prev_origin != new_origin {
self.borrow_mut().origin = ctx.window_origin();
ctx.invalidate_text_input(Some(ImeUpdate::LayoutChanged));
ctx.invalidate_text_input(ImeUpdate::LayoutChanged);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion druid/src/text/movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub enum Movement {
EndOfDocument,
}

//FIXME: we should remove this whole file, and use the Movement type defined in druid-shell?
impl From<crate::shell::text::Movement> for Movement {
fn from(src: crate::shell::text::Movement) -> Movement {
use crate::shell::text::{Direction, Movement as SMovemement, VerticalMovement};
Expand All @@ -63,7 +64,8 @@ impl From<crate::shell::text::Movement> for Movement {
| SMovemement::Vertical(VerticalMovement::PageDown) => Movement::Down,
SMovemement::Vertical(VerticalMovement::DocumentStart) => Movement::StartOfDocument,
SMovemement::Vertical(VerticalMovement::DocumentEnd) => Movement::EndOfDocument,
_ => unreachable!(),
// the source enum is non_exhaustive
_ => panic!("unhandled movement {:?}", src),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions druid/src/text/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ impl Selection {
}
}

//FIXME: delete this file, unify with druid-shell::text::Selection
impl From<Selection> for crate::shell::text::Selection {
fn from(src: Selection) -> crate::shell::text::Selection {
crate::shell::text::Selection {
Expand Down
54 changes: 10 additions & 44 deletions druid/src/widget/textbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ pub struct TextBox<T> {
inner: Padding<T, Scroll<T, TextComponent<T>>>,
scroll_to_selection_after_layout: bool,
multiline: bool,
wrap_lines: bool,
/// true if a click event caused us to gain focus.
///
/// On macOS, if focus happens via click then we set the selection based
Expand Down Expand Up @@ -78,7 +77,6 @@ impl<T: EditableText + TextStorage> TextBox<T> {
scroll_to_selection_after_layout: false,
placeholder,
multiline: false,
wrap_lines: false,
was_focused_from_click: false,
cursor_on: false,
cursor_timer: TimerToken::INVALID,
Expand Down Expand Up @@ -109,7 +107,6 @@ impl<T: EditableText + TextStorage> TextBox<T> {
///
/// [`multiline`]: TextBox::multiline
pub fn with_line_wrapping(mut self, wrap_lines: bool) -> Self {
self.wrap_lines = wrap_lines;
self.inner
.wrapped_mut()
.set_horizontal_scroll_enabled(!wrap_lines);
Expand Down Expand Up @@ -278,26 +275,6 @@ impl<T> TextBox<T> {
}

impl<T> TextBox<T> {
///// Set the textbox's selection.
//pub fn set_selection(&mut self, selection: Selection) {
//self.editor.set_selection(selection);
//}

///// Set the text and force the editor to update.
/////
///// This should be rarely needed; the main use-case would be if you need
///// to manually set the text and then immediately do hit-testing or other
///// tasks that rely on having an up-to-date text layout.
//pub fn force_rebuild(&mut self, text: T, factory: &mut PietText, env: &Env) {
//self.editor.set_text(text);
//self.editor.rebuild_if_needed(factory, env);
//}
}

impl<T> TextBox<T> {
//FIXME: maybe a more restrictive API? some kind of `with_text` method that
//takes a closure and makes sure we're locked, and then also notifies the platform
//of changes afterwards?
/// An immutable reference to the inner [`TextComponent`].
///
/// Using this correctly is difficult; please see the [`TextComponent`]
Expand Down Expand Up @@ -330,22 +307,11 @@ impl<T> TextBox<T> {

impl<T: TextStorage + EditableText> TextBox<T> {
fn rect_for_selection_end(&self) -> Rect {
let selection_end = self.text().borrow().selection().end;
let hit = self
.text()
.borrow()
.layout
.layout()
.unwrap()
.hit_test_text_position(selection_end);
let line = self
.text()
.borrow()
.layout
.layout()
.unwrap()
.line_metric(hit.line)
.unwrap();
let text = self.text().borrow();
let layout = text.layout.layout().unwrap();

let hit = layout.hit_test_text_position(text.selection().end);
let line = layout.line_metric(hit.line).unwrap();
let y0 = line.y_offset;
let y1 = y0 + line.height;
let x = hit.point.x;
Expand Down Expand Up @@ -427,7 +393,7 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
{
if self.text().borrow().set_clipboard() {
let inval = self.text_mut().borrow_mut().insert_text(data, "");
ctx.invalidate_text_input(Some(inval));
ctx.invalidate_text_input(inval);
}
ctx.set_handled();
}
Expand All @@ -440,7 +406,7 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
};
if !text.is_empty() {
let inval = self.text_mut().borrow_mut().insert_text(data, text);
ctx.invalidate_text_input(Some(inval));
ctx.invalidate_text_input(inval);
}
}
}
Expand All @@ -461,7 +427,7 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
if self.text().can_write() && !self.multiline && !self.was_focused_from_click {
let selection = Selection::new(0, data.len());
let _ = self.text_mut().borrow_mut().set_selection(selection);
ctx.invalidate_text_input(Some(druid_shell::text::Event::SelectionChanged));
ctx.invalidate_text_input(druid_shell::text::Event::SelectionChanged);
}
self.reset_cursor_blink(ctx.request_timer(CURSOR_BLINK_DURATION));
self.was_focused_from_click = false;
Expand All @@ -472,7 +438,7 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
let selection = self.text().borrow().selection();
let selection = Selection::new(selection.end, selection.end);
let _ = self.text_mut().borrow_mut().set_selection(selection);
ctx.invalidate_text_input(Some(druid_shell::text::Event::SelectionChanged));
ctx.invalidate_text_input(druid_shell::text::Event::SelectionChanged);
}
self.cursor_timer = TimerToken::INVALID;
self.was_focused_from_click = false;
Expand All @@ -492,7 +458,7 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
if self.text().can_write() {
if let Some(ime_invalidation) = self.text_mut().borrow_mut().pending_ime_invalidation()
{
ctx.invalidate_text_input(Some(ime_invalidation));
ctx.invalidate_text_input(ime_invalidation);
}
}
}
Expand Down
7 changes: 3 additions & 4 deletions druid/src/widget/value_textbox.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2018 The Druid Authors.
// Copyright 2021 The Druid Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -169,7 +169,7 @@ impl<T: Data> ValueTextBox<T> {
.borrow_mut()
.set_selection(Selection::new(0, self.buffer.len()))
{
ctx.invalidate_text_input(Some(inval));
ctx.invalidate_text_input(inval);
}
}
self.send_event(ctx, TextBoxEvent::Invalid(err));
Expand Down Expand Up @@ -304,7 +304,6 @@ impl<T: Data + std::fmt::Debug> Widget<T> for ValueTextBox<T> {
self.force_selection = new_sel;

if self.update_data_while_editing && !validation.is_err() {
//FIXME: notify platform of text change
if let Ok(new_data) = self.formatter.value(&self.buffer) {
*data = new_data;
self.last_known_data = Some(data.clone());
Expand Down Expand Up @@ -360,7 +359,7 @@ impl<T: Data + std::fmt::Debug> Widget<T> for ValueTextBox<T> {
if let Some(sel) = self.force_selection.take() {
if self.inner.text().can_write() {
if let Some(change) = self.inner.text_mut().borrow_mut().set_selection(sel) {
ctx.invalidate_text_input(Some(change));
ctx.invalidate_text_input(change);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion druid/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,9 @@ impl<T: Data> Window<T> {
self.focus = new;
// check if the newly focused widget has an IME session, and
// notify the system if so.
//FIXME: this is quadratic. do we care?
//
// If you're here because a profiler sent you: I guess I should've
// used a hashmap?
let old_was_ime = old
.map(|old| {
self.ime_handlers
Expand Down

0 comments on commit 98ece99

Please sign in to comment.