Chromium Code Reviews| 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); |