Index: components/exo/shell_surface.cc |
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc |
index 941bdbc94689b47889aeb6ddaaf3bf13f0c68226..0ea00afdbe8ec3c320d450c66199a1f4af519b98 100644 |
--- a/components/exo/shell_surface.cc |
+++ b/components/exo/shell_surface.cc |
@@ -4,11 +4,16 @@ |
#include "components/exo/shell_surface.h" |
+#include <algorithm> |
+ |
#include "ash/aura/wm_window_aura.h" |
+#include "ash/common/accessibility_delegate.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" |
+#include "ash/common/wm_shell.h" |
#include "ash/shell.h" |
#include "ash/wm/window_state_aura.h" |
#include "ash/wm/window_util.h" |
@@ -36,6 +41,10 @@ |
#include "ui/wm/core/shadow_types.h" |
#include "ui/wm/core/window_util.h" |
+#if defined(OS_CHROMEOS) |
+#include "chromeos/audio/chromeos_sounds.h" |
+#endif |
+ |
DECLARE_WINDOW_PROPERTY_TYPE(std::string*) |
namespace exo { |
@@ -86,7 +95,7 @@ class CustomFrameView : public views::NonClientFrameView { |
class CustomWindowTargeter : public aura::WindowTargeter { |
public: |
- CustomWindowTargeter() {} |
+ CustomWindowTargeter() : shadow_underlay_(nullptr) {} |
~CustomWindowTargeter() override {} |
// Overridden from aura::WindowTargeter: |
@@ -97,15 +106,45 @@ class CustomWindowTargeter : public aura::WindowTargeter { |
return false; |
gfx::Point local_point = event.location(); |
- if (window->parent()) |
reveman
2016/09/30 05:59:10
where did this check go? can you instead keep this
erosky
2016/09/30 18:38:40
ConvertPointToTarget already does the check, but I
|
- aura::Window::ConvertPointToTarget(window->parent(), window, |
+ |
+ // If the underlay is accepting events, test against it's bounds instead |
+ // since it will be larger than (and contain) the surface's bounds. |
+ if (shadow_underlay_ && !shadow_underlay_->ignore_events()) { |
+ aura::Window::ConvertPointToTarget(window->parent(), shadow_underlay_, |
&local_point); |
+ return gfx::Rect(shadow_underlay_->layer()->size()).Contains(local_point); |
+ } |
+ |
+ aura::Window::ConvertPointToTarget(window->parent(), window, &local_point); |
aura::Window::ConvertPointToTarget(window, surface->window(), &local_point); |
return surface->HitTestRect(gfx::Rect(local_point, gfx::Size(1, 1))); |
} |
+ ui::EventTarget* FindTargetForEvent(ui::EventTarget* root, |
reveman
2016/09/30 05:59:10
Why do we need to override this? What would it tak
erosky
2016/09/30 18:38:40
The default implementation clips the event at the
|
+ 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. |
+ 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); |
+ } |
+ |
+ void set_shadow_underlay(aura::Window* shadow_underlay) { |
+ shadow_underlay_ = shadow_underlay; |
+ } |
+ |
private: |
+ aura::Window* shadow_underlay_; |
reveman
2016/09/30 05:59:10
Instead of tracking this state here and having to
erosky
2016/09/30 18:38:40
It can't get out of sync because there is a 1-to-1
|
+ |
DISALLOW_COPY_AND_ASSIGN(CustomWindowTargeter); |
}; |
@@ -301,6 +340,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 +767,14 @@ gfx::Size ShellSurface::GetPreferredSize() const { |
} |
//////////////////////////////////////////////////////////////////////////////// |
+// ash::AccessibilityObserver overrides: |
+ |
+void ShellSurface::OnAccessibilityModeChanged( |
+ ash::AccessibilityNotificationVisibility) { |
+ UpdateShadow(); |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
// ash::wm::WindowStateObserver overrides: |
void ShellSurface::OnPreWindowStateTypeChange( |
@@ -821,12 +870,39 @@ void ShellSurface::OnWindowActivated( |
lost_active == widget_->GetNativeWindow()) { |
DCHECK(activatable_); |
Configure(); |
+ UpdateShadow(); |
} |
} |
//////////////////////////////////////////////////////////////////////////////// |
// ui::EventHandler overrides: |
+void ShellSurface::OnEvent(ui::Event* event) { |
+ // 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 |
reveman
2016/09/30 05:59:10
nit: s/spoken_feedback/spoken feedback/
erosky
2016/09/30 18:38:40
Done.
|
+ // behind-windows from receiving it) and play an earcon to notify the user. |
+ if (event->IsLocatedEvent() && event->target() == shadow_underlay_ && |
+ shadow_underlay_ && !shadow_underlay_->ignore_events()) { |
+#if defined(OS_CHROMEOS) |
+ const ui::EventType kEarconEventTypes[] = { |
reveman
2016/09/30 05:59:10
nit: are these elements sorted in a way that I'm f
erosky
2016/09/30 18:38:40
Done.
|
+ ui::ET_TOUCH_PRESSED, ui::ET_POINTER_DOWN, |
+ ui::ET_POINTER_WHEEL_CHANGED, ui::ET_SCROLL, |
+ ui::ET_SCROLL_FLING_START, ui::ET_GESTURE_BEGIN, |
+ ui::ET_MOUSE_PRESSED, ui::ET_MOUSEWHEEL}; |
+ 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(); |
+ return; |
+ } |
+ views::View::OnEvent(event); |
+} |
+ |
void ShellSurface::OnKeyEvent(ui::KeyEvent* event) { |
if (!resizer_) { |
views::View::OnKeyEvent(event); |
@@ -982,6 +1058,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 +1320,8 @@ void ShellSurface::UpdateShadow() { |
// Always create and show the underlay, even in maximized/fullscreen. |
if (!shadow_underlay_) { |
shadow_underlay_ = new aura::Window(nullptr); |
+ shadow_underlay_->SetTargetHandler(this); |
reveman
2016/09/30 05:59:10
Can we use a different aura::WindowDelegate for th
erosky
2016/09/30 18:38:40
Done.
|
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 |
@@ -1252,18 +1331,28 @@ void ShellSurface::UpdateShadow() { |
DCHECK(shadow_underlay_->layer()->fills_bounds_opaquely()); |
window->AddChild(shadow_underlay_); |
window->StackChildAtBottom(shadow_underlay_); |
+ static_cast<CustomWindowTargeter*>( |
+ static_cast<ui::EventTarget*>(window)->GetEventTargeter()) |
+ ->set_shadow_underlay(shadow_underlay_); |
} |
+ bool underlay_capture_events = ash::WmShell::Get() |
+ ->accessibility_delegate() |
+ ->IsSpokenFeedbackEnabled() && |
+ widget_->IsActive(); |
+ shadow_underlay_->set_ignore_events(!underlay_capture_events); |
+ |
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); |