From 6eb78273b8e8506f1d1f8da87820ebf731c7cd67 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 24 Jul 2026 12:44:17 +0900 Subject: [PATCH] Send view focus events and handle focus change requests The engine's view-focus embedder API (FlutterEngineSendViewFocusEvent, view_focus_change_request_callback) is declared in embedder.h but was never wired up, so PlatformDispatcher.onViewFocusChange and requestViewFocusChange did not work on Tizen. Since the embedder has no native window focus event handler, the app lifecycle is used as a proxy for view focus: kFocused is sent to the engine when the app is resumed, and kUnfocused when it becomes inactive or paused. Focus change requests from the framework are handled by activating the window. Windows created with focusable = false are marked with ecore_wl2_window_focus_skip_set and can never receive native key focus, so kFocused events and window activation are skipped for them. Fixes flutter-tizen/embedder#187 --- flutter/shell/platform/tizen/flutter_tizen.cc | 18 ++++++++++--- .../platform/tizen/flutter_tizen_engine.cc | 14 ++++++++++ .../platform/tizen/flutter_tizen_engine.h | 3 +++ .../platform/tizen/flutter_tizen_view.cc | 27 +++++++++++++++++++ .../shell/platform/tizen/flutter_tizen_view.h | 4 +++ flutter/shell/platform/tizen/tizen_window.h | 2 ++ 6 files changed, 65 insertions(+), 3 deletions(-) diff --git a/flutter/shell/platform/tizen/flutter_tizen.cc b/flutter/shell/platform/tizen/flutter_tizen.cc index a89a2574..91a29171 100644 --- a/flutter/shell/platform/tizen/flutter_tizen.cc +++ b/flutter/shell/platform/tizen/flutter_tizen.cc @@ -167,15 +167,27 @@ void FlutterDesktopEngineNotifyLowMemoryWarning( } void FlutterDesktopEngineNotifyAppIsInactive(FlutterDesktopEngineRef engine) { - EngineFromHandle(engine)->lifecycle_channel()->AppIsInactive(); + flutter::FlutterTizenEngine* tizen_engine = EngineFromHandle(engine); + tizen_engine->lifecycle_channel()->AppIsInactive(); + if (tizen_engine->view()) { + tizen_engine->view()->OnFocus(kUnfocused); + } } void FlutterDesktopEngineNotifyAppIsResumed(FlutterDesktopEngineRef engine) { - EngineFromHandle(engine)->lifecycle_channel()->AppIsResumed(); + flutter::FlutterTizenEngine* tizen_engine = EngineFromHandle(engine); + tizen_engine->lifecycle_channel()->AppIsResumed(); + if (tizen_engine->view()) { + tizen_engine->view()->OnFocus(kFocused); + } } void FlutterDesktopEngineNotifyAppIsPaused(FlutterDesktopEngineRef engine) { - EngineFromHandle(engine)->lifecycle_channel()->AppIsPaused(); + flutter::FlutterTizenEngine* tizen_engine = EngineFromHandle(engine); + tizen_engine->lifecycle_channel()->AppIsPaused(); + if (tizen_engine->view()) { + tizen_engine->view()->OnFocus(kUnfocused); + } } void FlutterDesktopEngineNotifyAppIsDetached(FlutterDesktopEngineRef engine) { diff --git a/flutter/shell/platform/tizen/flutter_tizen_engine.cc b/flutter/shell/platform/tizen/flutter_tizen_engine.cc index 3c3611ea..2de1a11a 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_engine.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_engine.cc @@ -195,6 +195,13 @@ bool FlutterTizenEngine::RunEngine() { auto* engine = static_cast(user_data); engine->OnUpdateSemantics(update); }; + args.view_focus_change_request_callback = + [](const FlutterViewFocusChangeRequest* request, void* user_data) { + auto* engine = static_cast(user_data); + if (engine->view()) { + engine->view()->OnFocusChangeRequest(*request); + } + }; if (IsHeaded() && dynamic_cast(renderer_.get())) { vsync_waiter_ = std::make_unique(this); @@ -341,6 +348,13 @@ void FlutterTizenEngine::SendPointerEvent(const FlutterPointerEvent& event) { embedder_api_.SendPointerEvent(engine_, &event, 1); } +void FlutterTizenEngine::SendViewFocusEvent( + const FlutterViewFocusEvent& event) { + if (engine_) { + embedder_api_.SendViewFocusEvent(engine_, &event); + } +} + void FlutterTizenEngine::SendWindowMetrics(int32_t x, int32_t y, int32_t width, diff --git a/flutter/shell/platform/tizen/flutter_tizen_engine.h b/flutter/shell/platform/tizen/flutter_tizen_engine.h index c6cfa341..ffe34d4d 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_engine.h +++ b/flutter/shell/platform/tizen/flutter_tizen_engine.h @@ -144,6 +144,9 @@ class FlutterTizenEngine { // Informs the engine of an incoming pointer event. void SendPointerEvent(const FlutterPointerEvent& event); + // Informs the engine of a native view focus change. + void SendViewFocusEvent(const FlutterViewFocusEvent& event); + // Sends a window metrics update to the Flutter engine using current window // dimensions in physical void SendWindowMetrics(int32_t x, diff --git a/flutter/shell/platform/tizen/flutter_tizen_view.cc b/flutter/shell/platform/tizen/flutter_tizen_view.cc index 030e12db..0be61bc8 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_view.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_view.cc @@ -151,6 +151,33 @@ void FlutterTizenView::OnRotate(int32_t degree) { SendWindowMetrics(geometry.left, geometry.top, width, height, 0.0); } +void FlutterTizenView::OnFocus(FlutterViewFocusState focus_state) { + if (focus_state == kFocused) { + auto* window = dynamic_cast(tizen_view_.get()); + if (window && !window->focusable()) { + return; + } + } + FlutterViewFocusEvent event = {}; + event.struct_size = sizeof(event); + event.view_id = view_id_; + event.state = focus_state; + event.direction = FlutterViewFocusDirection::kUndefined; + engine_->SendViewFocusEvent(event); +} + +void FlutterTizenView::OnFocusChangeRequest( + const FlutterViewFocusChangeRequest& request) { + if (request.view_id != view_id_ || request.state != kFocused) { + return; + } + if (auto* window = dynamic_cast(tizen_view_.get())) { + if (window->focusable()) { + window->ActivateWindow(); + } + } +} + FlutterTizenView::PointerState* FlutterTizenView::GetOrCreatePointerState( FlutterPointerDeviceKind device_kind, int32_t device_id) { diff --git a/flutter/shell/platform/tizen/flutter_tizen_view.h b/flutter/shell/platform/tizen/flutter_tizen_view.h index 4533acc8..362d2165 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_view.h +++ b/flutter/shell/platform/tizen/flutter_tizen_view.h @@ -51,6 +51,10 @@ class FlutterTizenView : public TizenViewEventHandlerDelegate { void OnRotate(int32_t degree) override; + void OnFocus(FlutterViewFocusState focus_state); + + void OnFocusChangeRequest(const FlutterViewFocusChangeRequest& request); + void OnPointerMove(double x, double y, size_t timestamp, diff --git a/flutter/shell/platform/tizen/tizen_window.h b/flutter/shell/platform/tizen/tizen_window.h index 90395bdc..a77b8508 100644 --- a/flutter/shell/platform/tizen/tizen_window.h +++ b/flutter/shell/platform/tizen/tizen_window.h @@ -36,6 +36,8 @@ class TizenWindow : public TizenViewBase { virtual void LowerWindow() = 0; + bool focusable() { return focusable_; } + protected: explicit TizenWindow(TizenGeometry geometry, bool transparent,