| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef COMPONENTS_EXO_TEXT_INPUT_H_ |
| #define COMPONENTS_EXO_TEXT_INPUT_H_ |
| |
| #include <optional> |
| #include <string> |
| |
| #include "base/i18n/rtl.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/scoped_observation.h" |
| #include "base/strings/string_piece.h" |
| #include "components/exo/seat_observer.h" |
| #include "ui/base/ime/ash/input_method_manager.h" |
| #include "ui/base/ime/autocorrect_info.h" |
| #include "ui/base/ime/composition_text.h" |
| #include "ui/base/ime/surrounding_text_tracker.h" |
| #include "ui/base/ime/text_input_client.h" |
| #include "ui/base/ime/text_input_flags.h" |
| #include "ui/base/ime/text_input_mode.h" |
| #include "ui/base/ime/text_input_type.h" |
| #include "ui/base/ime/virtual_keyboard_controller.h" |
| #include "ui/base/ime/virtual_keyboard_controller_observer.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/range/range.h" |
| |
| namespace ui { |
| class InputMethod; |
| } // namespace ui |
| |
| namespace exo { |
| class Surface; |
| class Seat; |
| |
| // This class bridges the ChromeOS input method and a text-input context. |
| // It can be inactive, active, or in a pending state where Activate() was |
| // called but the associated window is not focused. |
| class TextInput : public ui::TextInputClient, |
| public ui::VirtualKeyboardControllerObserver, |
| public ash::input_method::InputMethodManager::Observer, |
| public SeatObserver { |
| public: |
| class Delegate { |
| public: |
| virtual ~Delegate() = default; |
| |
| // Called when the text input session is activated. |
| virtual void Activated() = 0; |
| |
| // Called when the text input session is deactivated. TextInput does not |
| // refer to the delegate anymore. |
| virtual void Deactivated() = 0; |
| |
| // Called when the virtual keyboard visibility state has changed. |
| virtual void OnVirtualKeyboardVisibilityChanged(bool is_visible) = 0; |
| |
| // Called when the virtual keyboard's occluded bounds has changed. |
| // The bounds are in screen DIP. |
| virtual void OnVirtualKeyboardOccludedBoundsChanged( |
| const gfx::Rect& screen_bounds) = 0; |
| |
| // Returns true if the server can expect a finalize_virtual_keyboard_changes |
| // request from the client. |
| virtual bool SupportsFinalizeVirtualKeyboardChanges() = 0; |
| |
| // Set the 'composition text' of the current text input. |
| virtual void SetCompositionText(const ui::CompositionText& composition) = 0; |
| |
| // Commit |text| to the current text input session. |
| virtual void Commit(base::StringPiece16 text) = 0; |
| |
| // Set the cursor position. |
| // |surrounding_text| is the current surrounding text. |
| // The |selection| range is in UTF-16 offsets of the current surrounding |
| // text. |selection| must be a valid range, i.e. |
| // selection.IsValid() && selection.GetMax() <= surrounding_text.length(). |
| virtual void SetCursor(base::StringPiece16 surrounding_text, |
| const gfx::Range& selection) = 0; |
| |
| // Delete the surrounding text of the current text input. |
| // |surrounding_text| is the current surrounding text. |
| // The delete |range| is in UTF-16 offsets of the current surrounding text. |
| // |range| must be a valid range, i.e. |
| // range.IsValid() && range.GetMax() <= surrounding_text.length(). |
| virtual void DeleteSurroundingText(base::StringPiece16 surrounding_text, |
| const gfx::Range& range) = 0; |
| |
| // Sends a key event. |
| virtual void SendKey(const ui::KeyEvent& event) = 0; |
| |
| // Called when the text direction has changed. |
| virtual void OnTextDirectionChanged( |
| base::i18n::TextDirection direction) = 0; |
| |
| // Sets composition from the current surrounding text offsets. |
| // Offsets in |cursor| and |range| is relative to the beginning of |
| // |surrounding_text|. Offsets in |ui_ime_text_spans| is relative to the new |
| // composition, i.e. relative to |range|'s start. All offsets are in UTF16, |
| // and must be valid. |
| virtual void SetCompositionFromExistingText( |
| base::StringPiece16 surrounding_text, |
| const gfx::Range& cursor, |
| const gfx::Range& range, |
| const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) = 0; |
| |
| // Clears all the grammar fragments in |range|. |
| // |surrounding_text| is the current surrounding text, used for utf16 to |
| // utf8 conversion. |
| virtual void ClearGrammarFragments(base::StringPiece16 surrounding_text, |
| const gfx::Range& range) = 0; |
| |
| // Adds a new grammar marker according to |fragments|. Clients should show |
| // some visual indications such as underlining. |
| // |surrounding_text| is the current surrounding text, used for utf16 to |
| // utf8 conversion. |
| virtual void AddGrammarFragment(base::StringPiece16 surrounding_text, |
| const ui::GrammarFragment& fragment) = 0; |
| |
| // Sets the autocorrect range from the current surrounding text offsets. |
| // Offsets in |range| is relative to the beginning of |
| // |surrounding_text|. All offsets are in UTF16, and must be valid. |
| virtual void SetAutocorrectRange(base::StringPiece16 surrounding_text, |
| const gfx::Range& range) = 0; |
| |
| // Commits the current composition text. |
| // If `keep_selection` is true, keep the selection range unchanged. |
| // Otherwise, set the selection range to be after the committed text. |
| // Returns whether the operation is supported by the client. |
| virtual bool ConfirmComposition(bool keep_selection) = 0; |
| |
| // Does the current delegate support the new ConfirmComposition wayland |
| // method name confirm_preedit? |
| virtual bool SupportsConfirmPreedit() = 0; |
| |
| // Checks if InsertImage() is supported via wayland. |
| virtual bool HasImageInsertSupport() = 0; |
| |
| // Inserts image. |
| virtual void InsertImage(const GURL& src) = 0; |
| }; |
| |
| explicit TextInput(std::unique_ptr<Delegate> delegate); |
| TextInput(const TextInput&) = delete; |
| TextInput& operator=(const TextInput&) = delete; |
| ~TextInput() override; |
| |
| // Request to activate the text input context on the surface. Activation will |
| // occur immediately if the associated window is already focused, or |
| // otherwise when the window gains focus. |
| void Activate(Seat* seat, |
| Surface* surface, |
| ui::TextInputClient::FocusReason reason); |
| |
| // Deactivates the text input context. |
| void Deactivate(); |
| |
| // Shows the virtual keyboard if needed. |
| void ShowVirtualKeyboardIfEnabled(); |
| |
| // Hides the virtual keyboard. |
| void HideVirtualKeyboard(); |
| |
| // Re-synchronize the current status when the surrounding text has changed |
| // during the text input session. |
| void Resync(); |
| |
| // Resets the current input method composition state. |
| void Reset(); |
| |
| // Sets the surrounding text in the app. |
| // Ranges of |cursor_pos|, |grammar_fragment| and |autocorrect_info| are |
| // relative to |text|. |
| // |grammar_fragment| is the grammar fragment at the cursor position, |
| // if given. |
| // |autocorrect_info->bounds| is the bounding rect around the autocorrected |
| // text and is relative to the window origin. |
| void SetSurroundingText( |
| base::StringPiece16 text, |
| uint32_t offset, |
| const gfx::Range& cursor_pos, |
| const std::optional<ui::GrammarFragment>& grammar_fragment, |
| const std::optional<ui::AutocorrectInfo>& autocorrect_info); |
| |
| // Sets the text input type, mode, flags, |should_do_learning|, |
| // |can_compose_inline| and |surrounding_text_supported|. |
| void SetTypeModeFlags(ui::TextInputType type, |
| ui::TextInputMode mode, |
| int flags, |
| bool should_do_learning, |
| bool can_compose_inline, |
| bool surrounding_text_supported); |
| |
| // Sets the bounds of the text caret, relative to the window origin. |
| void SetCaretBounds(const gfx::Rect& bounds); |
| |
| // Finalizes pending virtual keyboard requested changes. |
| void FinalizeVirtualKeyboardChanges(); |
| |
| Delegate* delegate() { return delegate_.get(); } |
| |
| // ui::TextInputClient: |
| void SetCompositionText(const ui::CompositionText& composition) override; |
| size_t ConfirmCompositionText(bool keep_selection) override; |
| void ClearCompositionText() override; |
| void InsertText(const std::u16string& text, |
| InsertTextCursorBehavior cursor_behavior) override; |
| void InsertChar(const ui::KeyEvent& event) override; |
| bool CanInsertImage() override; |
| void InsertImage(const GURL& src) override; |
| ui::TextInputType GetTextInputType() const override; |
| ui::TextInputMode GetTextInputMode() const override; |
| base::i18n::TextDirection GetTextDirection() const override; |
| int GetTextInputFlags() const override; |
| bool CanComposeInline() const override; |
| gfx::Rect GetCaretBounds() const override; |
| gfx::Rect GetSelectionBoundingBox() const override; |
| bool GetCompositionCharacterBounds(size_t index, |
| gfx::Rect* rect) const override; |
| bool HasCompositionText() const override; |
| ui::TextInputClient::FocusReason GetFocusReason() const override; |
| bool GetTextRange(gfx::Range* range) const override; |
| bool GetCompositionTextRange(gfx::Range* range) const override; |
| bool GetEditableSelectionRange(gfx::Range* range) const override; |
| bool SetEditableSelectionRange(const gfx::Range& range) override; |
| bool GetTextFromRange(const gfx::Range& range, |
| std::u16string* text) const override; |
| void OnInputMethodChanged() override; |
| bool ChangeTextDirectionAndLayoutAlignment( |
| base::i18n::TextDirection direction) override; |
| void ExtendSelectionAndDelete(size_t before, size_t after) override; |
| void ExtendSelectionAndReplace(size_t before, |
| size_t after, |
| base::StringPiece16 replacement_text) override; |
| void EnsureCaretNotInRect(const gfx::Rect& rect) override; |
| bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override; |
| void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override; |
| ukm::SourceId GetClientSourceForMetrics() const override; |
| bool ShouldDoLearning() override; |
| bool SetCompositionFromExistingText( |
| const gfx::Range& range, |
| const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) override; |
| gfx::Range GetAutocorrectRange() const override; |
| gfx::Rect GetAutocorrectCharacterBounds() const override; |
| bool SetAutocorrectRange(const gfx::Range& range) override; |
| std::optional<ui::GrammarFragment> GetGrammarFragmentAtCursor() |
| const override; |
| bool ClearGrammarFragments(const gfx::Range& range) override; |
| bool AddGrammarFragments( |
| const std::vector<ui::GrammarFragment>& fragments) override; |
| bool SupportsAlwaysConfirmComposition() override; |
| void GetActiveTextInputControlLayoutBounds( |
| std::optional<gfx::Rect>* control_bounds, |
| std::optional<gfx::Rect>* selection_bounds) override {} |
| |
| // ui::VirtualKeyboardControllerObserver: |
| void OnKeyboardVisible(const gfx::Rect& keyboard_rect) override; |
| void OnKeyboardHidden() override; |
| |
| // ash::input_method::InputMethodManager::Observer: |
| void InputMethodChanged(ash::input_method::InputMethodManager* manager, |
| Profile* profile, |
| bool show_message) override; |
| |
| // SeatObserver: |
| void OnSurfaceFocused(Surface* gained_focus, |
| Surface* lost_focus, |
| bool has_focused_surface) override; |
| |
| private: |
| void AttachInputMethod(); |
| void DetachInputMethod(); |
| void ResetCompositionTextCache(); |
| |
| bool ShouldStageVKState(); |
| void SendStagedVKVisibility(); |
| void SendStagedVKOccludedBounds(); |
| |
| // Delegate to talk to actual its client. |
| std::unique_ptr<Delegate> delegate_; |
| |
| // On requesting to show Virtual Keyboard, InputMethod may not be connected. |
| // So, remember the request temporarily, and then on InputMethod connection |
| // show the Virtual Keyboard. |
| bool pending_vk_visible_ = false; |
| |
| // |surface_| and |seat_| are non-null if and only if the TextInput is in a |
| // pending or active state, in which case the TextInput will be observing the |
| // Seat. |
| raw_ptr<Surface, DanglingUntriaged> surface_ = nullptr; |
| raw_ptr<Seat, DanglingUntriaged> seat_ = nullptr; |
| |
| // If the TextInput is active (associated window has focus) and the |
| // InputMethod is available, this is set and the TextInput will be its |
| // focused client. Otherwise, it is null and the TextInput is not attached |
| // to any InputMethod, so the TextInputClient overrides will not be called. |
| raw_ptr<ui::InputMethod, DanglingUntriaged> input_method_ = nullptr; |
| |
| base::ScopedObservation<ash::input_method::InputMethodManager, |
| ash::input_method::InputMethodManager::Observer> |
| input_method_manager_observation_{this}; |
| |
| base::ScopedObservation<ui::VirtualKeyboardController, |
| ui::VirtualKeyboardControllerObserver> |
| virtual_keyboard_observation_{this}; |
| |
| // Cache of the current caret bounding box, sent from the client. |
| gfx::Rect caret_bounds_; |
| |
| // Cache of the current input field attributes sent from the client. |
| ui::TextInputType input_type_ = ui::TEXT_INPUT_TYPE_NONE; |
| ui::TextInputMode input_mode_ = ui::TEXT_INPUT_MODE_DEFAULT; |
| int flags_ = ui::TEXT_INPUT_FLAG_NONE; |
| bool should_do_learning_ = true; |
| bool can_compose_inline_ = true; |
| ui::TextInputClient::FocusReason focus_reason_ = |
| ui::TextInputClient::FOCUS_REASON_NONE; |
| |
| // Whether the client supports surrounding text. |
| bool surrounding_text_supported_ = true; |
| // If surrounding text is not supported and the active IME needs it, we force |
| // using TEXT_INPUT_TYPE_NULL. |
| bool use_null_input_type_ = false; |
| |
| // Tracks the surrounding text. |
| ui::SurroundingTextTracker surrounding_text_tracker_; |
| |
| // Cache of the current text input direction, update from the Chrome OS IME. |
| base::i18n::TextDirection direction_ = base::i18n::UNKNOWN_DIRECTION; |
| |
| // Cache of the grammar fragment at cursor position, send from Lacros side. |
| std::optional<ui::GrammarFragment> grammar_fragment_at_cursor_; |
| |
| // Latest autocorrect information that was sent from the Wayland client. |
| // along with the last surrounding text change. |
| ui::AutocorrectInfo autocorrect_info_; |
| |
| // True when client has made virtual keyboard related requests but haven't |
| // sent the virtual keyboard finalize request. |
| bool pending_vk_finalize_ = false; |
| // Holds the vk visibility to send to the client. |
| std::optional<bool> staged_vk_visible_; |
| // Holds the vk occluded bounds to send to the client. |
| std::optional<gfx::Rect> staged_vk_occluded_bounds_; |
| }; |
| |
| } // namespace exo |
| |
| #endif // COMPONENTS_EXO_TEXT_INPUT_H_ |