Index: components/exo/shell_surface.cc |
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc |
index 465477e79c488da48e2b777eaafd631ae51591a8..8952f1cb3b665d988d0ed99d055fc12f8849ab67 100644 |
--- a/components/exo/shell_surface.cc |
+++ b/components/exo/shell_surface.cc |
@@ -4,9 +4,13 @@ |
#include "components/exo/shell_surface.h" |
+#include <algorithm> |
+ |
#include "ash/aura/wm_window_aura.h" |
+#include "ash/common/accessibility_delegate.h" |
#include "ash/common/shelf/wm_shelf.h" |
#include "ash/common/shell_window_ids.h" |
+#include "ash/common/system/tray/system_tray_notifier.h" |
#include "ash/common/wm/window_resizer.h" |
#include "ash/common/wm/window_state.h" |
#include "ash/common/wm/window_state_delegate.h" |
@@ -37,6 +41,10 @@ |
#include "ui/wm/core/shadow_types.h" |
#include "ui/wm/core/window_util.h" |
+#if defined(OS_CHROMEOS) |
oshima
2016/10/01 02:00:09
Do you need this? I believe this file is compiled
reveman
2016/10/01 08:12:30
Compiling this only on ChromeOS is by choice and t
|
+#include "chromeos/audio/chromeos_sounds.h" |
+#endif |
+ |
DECLARE_WINDOW_PROPERTY_TYPE(std::string*) |
namespace exo { |
@@ -87,7 +95,7 @@ class CustomFrameView : public views::NonClientFrameView { |
class CustomWindowTargeter : public aura::WindowTargeter { |
public: |
- CustomWindowTargeter() {} |
+ CustomWindowTargeter(views::Widget* widget) : widget_(widget) {} |
~CustomWindowTargeter() override {} |
// Overridden from aura::WindowTargeter: |
@@ -98,6 +106,20 @@ class CustomWindowTargeter : public aura::WindowTargeter { |
return false; |
gfx::Point local_point = event.location(); |
+ |
+ // If the underlay is accepting events, test against it's bounds instead |
+ // since it will be larger than (and contain) the surface's bounds. |
+ aura::Window* shadow_underlay = |
+ static_cast<ShellSurface*>( |
+ widget_->widget_delegate()->GetContentsView()) |
+ ->shadow_underlay(); |
+ if (shadow_underlay && !shadow_underlay->ignore_events()) { |
+ if (window->parent()) |
+ aura::Window::ConvertPointToTarget(window->parent(), shadow_underlay, |
+ &local_point); |
+ return gfx::Rect(shadow_underlay->layer()->size()).Contains(local_point); |
+ } |
+ |
if (window->parent()) |
aura::Window::ConvertPointToTarget(window->parent(), window, |
&local_point); |
@@ -106,7 +128,30 @@ class CustomWindowTargeter : public aura::WindowTargeter { |
return surface->HitTestRect(gfx::Rect(local_point, gfx::Size(1, 1))); |
} |
+ ui::EventTarget* FindTargetForEvent(ui::EventTarget* root, |
+ ui::Event* event) override { |
+ aura::Window* window = static_cast<aura::Window*>(root); |
+ Surface* surface = ShellSurface::GetMainSurface(window); |
+ |
+ // Send events which are outside of the surface's bounds to the underlay. |
+ aura::Window* shadow_underlay = |
+ static_cast<ShellSurface*>( |
+ widget_->widget_delegate()->GetContentsView()) |
+ ->shadow_underlay(); |
+ if (surface && event->IsLocatedEvent() && shadow_underlay && |
+ !shadow_underlay->ignore_events()) { |
+ gfx::Point local_point = event->AsLocatedEvent()->location(); |
+ aura::Window::ConvertPointToTarget(window, surface->window(), |
+ &local_point); |
+ if (!surface->HitTestRect(gfx::Rect(local_point, gfx::Size(1, 1)))) |
+ return shadow_underlay; |
+ } |
+ return aura::WindowTargeter::FindTargetForEvent(root, event); |
+ } |
+ |
private: |
+ views::Widget* const widget_; |
+ |
DISALLOW_COPY_AND_ASSIGN(CustomWindowTargeter); |
}; |
@@ -166,6 +211,42 @@ class ShellSurfaceWidget : public views::Widget { |
DISALLOW_COPY_AND_ASSIGN(ShellSurfaceWidget); |
}; |
+class ShadowUnderlayEventHandler : public ui::EventHandler { |
+ public: |
+ ShadowUnderlayEventHandler() {} |
+ ~ShadowUnderlayEventHandler() override {} |
+ |
+ // Overridden from ui::EventHandler: |
+ void OnEvent(ui::Event* event) override { |
+ // If the event is targeted at the underlay, it means the user has made an |
+ // interaction that is outside the surface's bounds and we want to capture |
+ // it (usually when in spoken feedback mode). Handle the event (to prevent |
+ // behind-windows from receiving it) and play an earcon to notify the user. |
+ if (event->IsLocatedEvent()) { |
+#if defined(OS_CHROMEOS) |
+ const ui::EventType kEarconEventTypes[] = {ui::ET_MOUSE_PRESSED, |
+ ui::ET_MOUSEWHEEL, |
+ ui::ET_TOUCH_PRESSED, |
+ ui::ET_POINTER_DOWN, |
+ ui::ET_POINTER_WHEEL_CHANGED, |
+ ui::ET_GESTURE_BEGIN, |
+ ui::ET_SCROLL, |
+ ui::ET_SCROLL_FLING_START}; |
+ bool is_earcon_event_type = |
+ std::find(std::begin(kEarconEventTypes), std::end(kEarconEventTypes), |
+ event->type()) != std::end(kEarconEventTypes); |
+ if (is_earcon_event_type) |
+ ash::WmShell::Get()->accessibility_delegate()->PlayEarcon( |
+ chromeos::SOUND_VOLUME_ADJUST); |
+#endif |
+ event->SetHandled(); |
+ } |
+ } |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(ShadowUnderlayEventHandler); |
+}; |
+ |
} // namespace |
// Helper class used to coalesce a number of changes into one "configure" |
@@ -302,6 +383,8 @@ ShellSurface::~ShellSurface() { |
surface_->SetSurfaceDelegate(nullptr); |
surface_->RemoveSurfaceObserver(this); |
} |
+ ash::WmShell::Get()->system_tray_notifier()->RemoveAccessibilityObserver( |
+ this); |
} |
void ShellSurface::AcknowledgeConfigure(uint32_t serial) { |
@@ -726,6 +809,14 @@ gfx::Size ShellSurface::GetPreferredSize() const { |
} |
//////////////////////////////////////////////////////////////////////////////// |
+// ash::AccessibilityObserver overrides: |
+ |
+void ShellSurface::OnAccessibilityModeChanged( |
+ ash::AccessibilityNotificationVisibility) { |
+ UpdateShadow(); |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
// ash::wm::WindowStateObserver overrides: |
void ShellSurface::OnPreWindowStateTypeChange( |
@@ -821,6 +912,7 @@ void ShellSurface::OnWindowActivated( |
lost_active == widget_->GetNativeWindow()) { |
DCHECK(activatable_); |
Configure(); |
+ UpdateShadow(); |
} |
} |
@@ -936,7 +1028,7 @@ void ShellSurface::CreateShellSurfaceWidget(ui::WindowShowState show_state) { |
aura::Window* window = widget_->GetNativeWindow(); |
window->SetName("ExoShellSurface"); |
window->AddChild(surface_->window()); |
- window->SetEventTargeter(base::WrapUnique(new CustomWindowTargeter)); |
+ window->SetEventTargeter(base::WrapUnique(new CustomWindowTargeter(widget_))); |
SetApplicationId(window, &application_id_); |
SetMainSurface(window, surface_); |
@@ -982,6 +1074,9 @@ void ShellSurface::CreateShellSurfaceWidget(ui::WindowShowState show_state) { |
window_state->SetDelegate(std::unique_ptr<ash::wm::WindowStateDelegate>( |
new CustomWindowStateDelegate(widget_))); |
+ // Receive accessibility changes to update shadow underlay. |
+ ash::WmShell::Get()->system_tray_notifier()->AddAccessibilityObserver(this); |
+ |
// Show widget next time Commit() is called. |
pending_show_widget_ = true; |
} |
@@ -1241,8 +1336,10 @@ void ShellSurface::UpdateShadow() { |
// Always create and show the underlay, even in maximized/fullscreen. |
if (!shadow_underlay_) { |
shadow_underlay_ = new aura::Window(nullptr); |
+ shadow_underlay_event_handler_ = |
+ base::MakeUnique<ShadowUnderlayEventHandler>(); |
+ shadow_underlay_->SetTargetHandler(shadow_underlay_event_handler_.get()); |
DCHECK(shadow_underlay_->owned_by_parent()); |
- shadow_underlay_->set_ignore_events(true); |
// Ensure the background area inside the shadow is solid black. |
// Clients that provide translucent contents should not be using |
// rectangular shadows as this method requires opaque contents to |
@@ -1254,16 +1351,23 @@ void ShellSurface::UpdateShadow() { |
window->StackChildAtBottom(shadow_underlay_); |
} |
+ bool underlay_capture_events = ash::WmShell::Get() |
+ ->accessibility_delegate() |
+ ->IsSpokenFeedbackEnabled() && |
+ widget_->IsActive(); |
+ shadow_underlay_->set_ignore_events(!underlay_capture_events); |
oshima
2016/10/01 02:00:10
I think shadow underlay should always consume even
reveman
2016/10/01 08:12:30
+1
Mr4D (OOO till 08-26)
2016/10/01 08:56:21
The purpose of this is that blind people can touch
erosky
2016/10/03 17:44:02
Doing that would include things like immersive-mod
oshima
2016/10/03 19:57:01
Yes, we want this for both cases.
erosky
2016/10/03 22:35:08
Done.
|
+ |
float shadow_underlay_opacity = rectangular_shadow_background_opacity_; |
// Put the black background layer behind the window if |
- // 1) the window is in immersive fullscreen. |
+ // 1) the window is in immersive fullscreen or is active with |
+ // spoken feedback enabled. |
// 2) the window can control the bounds of the window in fullscreen ( |
// thus the background can be visible). |
// 3) the window has no transform (the transformed background may |
// not cover the entire background, e.g. overview mode). |
- if (widget_->IsFullscreen() && |
+ if ((widget_->IsFullscreen() || underlay_capture_events) && |
ash::wm::GetWindowState(window)->allow_set_bounds_in_maximized() && |
- window->layer()->transform().IsIdentity()) { |
+ window->layer()->GetTargetTransform().IsIdentity()) { |
gfx::Point origin; |
origin -= window->bounds().origin().OffsetFromOrigin(); |
shadow_bounds.set_origin(origin); |