Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.h" | |
| 6 | |
| 7 #include "apps/ui/views/app_window_frame_view.h" | |
| 8 #include "ash/ash_constants.h" | |
| 9 #include "ash/frame/custom_frame_view_ash.h" | |
| 10 #include "ash/screen_util.h" | |
| 11 #include "ash/shell.h" | |
| 12 #include "ash/wm/immersive_fullscreen_controller.h" | |
| 13 #include "ash/wm/panels/panel_frame_view.h" | |
| 14 #include "ash/wm/window_properties.h" | |
| 15 #include "ash/wm/window_state.h" | |
| 16 #include "ash/wm/window_state_delegate.h" | |
| 17 #include "ash/wm/window_state_observer.h" | |
| 18 #include "chrome/browser/ui/ash/ash_util.h" | |
| 19 #include "chrome/browser/ui/ash/multi_user/multi_user_context_menu.h" | |
| 20 #include "chrome/browser/ui/host_desktop.h" | |
| 21 #include "chrome/browser/ui/views/apps/shaped_app_window_targeter.h" | |
| 22 #include "ui/aura/client/aura_constants.h" | |
| 23 #include "ui/aura/window.h" | |
| 24 #include "ui/aura/window_observer.h" | |
| 25 #include "ui/base/hit_test.h" | |
| 26 #include "ui/base/models/simple_menu_model.h" | |
| 27 #include "ui/gfx/image/image_skia.h" | |
| 28 #include "ui/views/controls/menu/menu_runner.h" | |
| 29 #include "ui/views/widget/widget.h" | |
| 30 #include "ui/wm/core/easy_resize_window_targeter.h" | |
| 31 | |
| 32 #if defined(OS_CHROMEOS) | |
| 33 #include "ash/shell_window_ids.h" | |
| 34 #endif | |
| 35 | |
| 36 using extensions::AppWindow; | |
| 37 | |
| 38 namespace { | |
| 39 | |
| 40 // This class handles a user's fullscreen request (Shift+F4/F4). | |
| 41 class NativeAppWindowStateDelegate : public ash::wm::WindowStateDelegate, | |
| 42 public ash::wm::WindowStateObserver, | |
| 43 public aura::WindowObserver { | |
| 44 public: | |
| 45 NativeAppWindowStateDelegate(AppWindow* app_window, | |
| 46 extensions::NativeAppWindow* native_app_window) | |
| 47 : app_window_(app_window), | |
| 48 window_state_( | |
| 49 ash::wm::GetWindowState(native_app_window->GetNativeWindow())) { | |
| 50 // Add a window state observer to exit fullscreen properly in case | |
| 51 // fullscreen is exited without going through AppWindow::Restore(). This | |
| 52 // is the case when exiting immersive fullscreen via the "Restore" window | |
| 53 // control. | |
| 54 // TODO(pkotwicz): This is a hack. Remove ASAP. http://crbug.com/319048 | |
| 55 window_state_->AddObserver(this); | |
| 56 window_state_->window()->AddObserver(this); | |
| 57 } | |
| 58 ~NativeAppWindowStateDelegate() override { | |
| 59 if (window_state_) { | |
| 60 window_state_->RemoveObserver(this); | |
| 61 window_state_->window()->RemoveObserver(this); | |
| 62 } | |
| 63 } | |
| 64 | |
| 65 private: | |
| 66 // Overridden from ash::wm::WindowStateDelegate. | |
| 67 bool ToggleFullscreen(ash::wm::WindowState* window_state) override { | |
| 68 // Windows which cannot be maximized should not be fullscreened. | |
| 69 DCHECK(window_state->IsFullscreen() || window_state->CanMaximize()); | |
| 70 if (window_state->IsFullscreen()) | |
| 71 app_window_->Restore(); | |
| 72 else if (window_state->CanMaximize()) | |
| 73 app_window_->OSFullscreen(); | |
| 74 return true; | |
| 75 } | |
| 76 | |
| 77 // Overridden from ash::wm::WindowStateObserver: | |
| 78 void OnPostWindowStateTypeChange(ash::wm::WindowState* window_state, | |
| 79 ash::wm::WindowStateType old_type) override { | |
| 80 // Since the window state might get set by a window manager, it is possible | |
| 81 // to come here before the application set its |BaseWindow|. | |
| 82 if (!window_state->IsFullscreen() && !window_state->IsMinimized() && | |
| 83 app_window_->GetBaseWindow() && | |
| 84 app_window_->GetBaseWindow()->IsFullscreenOrPending()) { | |
| 85 app_window_->Restore(); | |
| 86 // Usually OnNativeWindowChanged() is called when the window bounds are | |
| 87 // changed as a result of a state type change. Because the change in state | |
| 88 // type has already occurred, we need to call OnNativeWindowChanged() | |
| 89 // explicitly. | |
| 90 app_window_->OnNativeWindowChanged(); | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 // Overridden from aura::WindowObserver: | |
| 95 void OnWindowDestroying(aura::Window* window) override { | |
| 96 window_state_->RemoveObserver(this); | |
| 97 window_state_->window()->RemoveObserver(this); | |
| 98 window_state_ = NULL; | |
| 99 } | |
| 100 | |
| 101 // Not owned. | |
| 102 AppWindow* app_window_; | |
| 103 ash::wm::WindowState* window_state_; | |
| 104 | |
| 105 DISALLOW_COPY_AND_ASSIGN(NativeAppWindowStateDelegate); | |
| 106 }; | |
| 107 | |
| 108 } // namespace | |
| 109 | |
| 110 ChromeNativeAppWindowViewsAura::ChromeNativeAppWindowViewsAura() { | |
| 111 } | |
| 112 | |
| 113 ChromeNativeAppWindowViewsAura::~ChromeNativeAppWindowViewsAura() { | |
| 114 } | |
| 115 | |
| 116 gfx::NativeView ChromeNativeAppWindowViewsAura::GetImeWindowContainer() { | |
| 117 #if defined(OS_CHROMEOS) | |
| 118 return ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(), | |
| 119 ash::kShellWindowId_ImeWindowParentContainer); | |
| 120 #else | |
| 121 return ChromeNativeAppWindowViews::GetImeWindowContainer(); | |
| 122 #endif | |
| 123 } | |
| 124 | |
| 125 gfx::Rect ChromeNativeAppWindowViewsAura::GetPanelWindowInitialBounds( | |
| 126 gfx::Size preferred_size_) { | |
| 127 if (ash::Shell::HasInstance()) { | |
| 128 // Open a new panel on the target root. | |
| 129 return ash::ScreenUtil::ConvertRectToScreen( | |
| 130 ash::Shell::GetTargetRootWindow(), gfx::Rect(preferred_size_)); | |
| 131 } | |
| 132 | |
| 133 return gfx::Rect(preferred_size_); | |
| 134 } | |
| 135 | |
| 136 apps::AppWindowFrameView* | |
| 137 ChromeNativeAppWindowViewsAura::CreateNonStandardAppFrame() { | |
| 138 apps::AppWindowFrameView* frame = | |
| 139 ChromeNativeAppWindowViews::CreateNonStandardAppFrame(); | |
|
tapted
2015/02/25 05:54:35
It might be nicer to have something like
void OnA
| |
| 140 | |
| 141 // For Aura windows on the Ash desktop the sizes are different and the user | |
| 142 // can resize the window from slightly outside the bounds as well. | |
| 143 if (chrome::IsNativeWindowInAsh(widget()->GetNativeWindow())) { | |
| 144 frame->SetResizeSizes(ash::kResizeInsideBoundsSize, | |
| 145 ash::kResizeOutsideBoundsSize, | |
| 146 ash::kResizeAreaCornerSize); | |
| 147 } | |
| 148 | |
| 149 #if !defined(OS_CHROMEOS) | |
| 150 // For non-Ash windows, install an easy resize window targeter, which ensures | |
| 151 // that the root window (not the app) receives mouse events on the edges. | |
| 152 if (chrome::GetHostDesktopTypeForNativeWindow(widget()->GetNativeWindow()) != | |
| 153 chrome::HOST_DESKTOP_TYPE_ASH) { | |
| 154 aura::Window* window = widget()->GetNativeWindow(); | |
| 155 int resize_inside = frame->resize_inside_bounds_size(); | |
| 156 gfx::Insets inset(resize_inside, resize_inside, resize_inside, | |
| 157 resize_inside); | |
| 158 // Add the EasyResizeWindowTargeter on the window, not its root window. The | |
| 159 // root window does not have a delegate, which is needed to handle the event | |
| 160 // in Linux. | |
| 161 window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( | |
| 162 new wm::EasyResizeWindowTargeter(window, inset, inset))); | |
| 163 } | |
| 164 #endif | |
| 165 | |
| 166 return frame; | |
| 167 } | |
| 168 | |
| 169 gfx::Rect ChromeNativeAppWindowViewsAura::GetRestoredBounds() const { | |
| 170 gfx::Rect* bounds = | |
| 171 widget()->GetNativeWindow()->GetProperty(ash::kRestoreBoundsOverrideKey); | |
| 172 if (bounds && !bounds->IsEmpty()) | |
| 173 return *bounds; | |
| 174 | |
| 175 return ChromeNativeAppWindowViews::GetRestoredBounds(); | |
| 176 } | |
| 177 | |
| 178 ui::WindowShowState ChromeNativeAppWindowViewsAura::GetRestoredState() const { | |
| 179 // Use kRestoreShowStateKey in case a window is minimized/hidden. | |
| 180 ui::WindowShowState restore_state = widget()->GetNativeWindow()->GetProperty( | |
| 181 aura::client::kRestoreShowStateKey); | |
| 182 if (widget()->GetNativeWindow()->GetProperty( | |
| 183 ash::kRestoreBoundsOverrideKey)) { | |
| 184 // If an override is given, we use that restore state (after filtering). | |
| 185 restore_state = widget()->GetNativeWindow()->GetProperty( | |
| 186 ash::kRestoreShowStateOverrideKey); | |
| 187 } else { | |
| 188 // Otherwise first normal states are checked. | |
| 189 if (IsMaximized()) | |
| 190 return ui::SHOW_STATE_MAXIMIZED; | |
| 191 if (IsFullscreen()) { | |
| 192 if (immersive_fullscreen_controller_.get() && | |
| 193 immersive_fullscreen_controller_->IsEnabled()) { | |
| 194 // Restore windows which were previously in immersive fullscreen to | |
| 195 // maximized. Restoring the window to a different fullscreen type | |
| 196 // makes for a bad experience. | |
| 197 return ui::SHOW_STATE_MAXIMIZED; | |
| 198 } | |
| 199 return ui::SHOW_STATE_FULLSCREEN; | |
| 200 } | |
| 201 } | |
| 202 // Whitelist states to return so that invalid and transient states | |
| 203 // are not saved and used to restore windows when they are recreated. | |
| 204 switch (restore_state) { | |
| 205 case ui::SHOW_STATE_NORMAL: | |
| 206 case ui::SHOW_STATE_MAXIMIZED: | |
| 207 case ui::SHOW_STATE_FULLSCREEN: | |
| 208 return restore_state; | |
| 209 | |
| 210 case ui::SHOW_STATE_DEFAULT: | |
| 211 case ui::SHOW_STATE_MINIMIZED: | |
| 212 case ui::SHOW_STATE_INACTIVE: | |
| 213 case ui::SHOW_STATE_END: | |
| 214 return ui::SHOW_STATE_NORMAL; | |
| 215 } | |
| 216 | |
| 217 return ui::SHOW_STATE_NORMAL; | |
| 218 } | |
| 219 | |
| 220 bool ChromeNativeAppWindowViewsAura::IsAlwaysOnTop() const { | |
| 221 if (app_window()->window_type_is_panel()) { | |
| 222 return ash::wm::GetWindowState(widget()->GetNativeWindow()) | |
| 223 ->panel_attached(); | |
| 224 } else { | |
|
tapted
2015/02/25 05:54:35
nit: "no else after return"
perhaps ternary-if-el
jackhou1
2015/02/25 22:57:51
Done.
| |
| 225 return widget()->IsAlwaysOnTop(); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 void ChromeNativeAppWindowViewsAura::ShowContextMenuForView( | |
| 230 views::View* source, | |
| 231 const gfx::Point& p, | |
| 232 ui::MenuSourceType source_type) { | |
| 233 #if defined(OS_CHROMEOS) | |
| 234 scoped_ptr<ui::MenuModel> model = | |
| 235 CreateMultiUserContextMenu(app_window()->GetNativeWindow()); | |
| 236 if (!model.get()) | |
| 237 return; | |
| 238 | |
| 239 // Only show context menu if point is in caption. | |
| 240 gfx::Point point_in_view_coords(p); | |
| 241 views::View::ConvertPointFromScreen(widget()->non_client_view(), | |
| 242 &point_in_view_coords); | |
| 243 int hit_test = | |
| 244 widget()->non_client_view()->NonClientHitTest(point_in_view_coords); | |
| 245 if (hit_test == HTCAPTION) { | |
| 246 menu_runner_.reset(new views::MenuRunner( | |
| 247 model.get(), | |
| 248 views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU)); | |
| 249 if (menu_runner_->RunMenuAt(source->GetWidget(), NULL, | |
| 250 gfx::Rect(p, gfx::Size(0, 0)), | |
| 251 views::MENU_ANCHOR_TOPLEFT, source_type) == | |
| 252 views::MenuRunner::MENU_DELETED) { | |
| 253 return; | |
| 254 } | |
| 255 } | |
| 256 #endif | |
| 257 } | |
| 258 | |
| 259 views::NonClientFrameView* | |
| 260 ChromeNativeAppWindowViewsAura::CreateNonClientFrameView( | |
| 261 views::Widget* widget) { | |
| 262 if (chrome::IsNativeViewInAsh(widget->GetNativeView())) { | |
| 263 // Set the delegate now because CustomFrameViewAsh sets the | |
| 264 // WindowStateDelegate if one is not already set. | |
| 265 ash::wm::GetWindowState(GetNativeWindow()) | |
| 266 ->SetDelegate( | |
| 267 scoped_ptr<ash::wm::WindowStateDelegate>( | |
| 268 new NativeAppWindowStateDelegate(app_window(), this)).Pass()); | |
| 269 | |
| 270 if (IsFrameless()) | |
| 271 return CreateNonStandardAppFrame(); | |
| 272 | |
| 273 if (app_window()->window_type_is_panel()) { | |
| 274 views::NonClientFrameView* frame_view = | |
| 275 new ash::PanelFrameView(widget, ash::PanelFrameView::FRAME_ASH); | |
| 276 frame_view->set_context_menu_controller(this); | |
| 277 return frame_view; | |
| 278 } | |
| 279 | |
| 280 ash::CustomFrameViewAsh* custom_frame_view = | |
| 281 new ash::CustomFrameViewAsh(widget); | |
| 282 // Non-frameless app windows can be put into immersive fullscreen. | |
| 283 immersive_fullscreen_controller_.reset( | |
| 284 new ash::ImmersiveFullscreenController()); | |
| 285 custom_frame_view->InitImmersiveFullscreenControllerForView( | |
| 286 immersive_fullscreen_controller_.get()); | |
| 287 custom_frame_view->GetHeaderView()->set_context_menu_controller(this); | |
| 288 | |
| 289 if (HasFrameColor()) { | |
| 290 custom_frame_view->SetFrameColors(ActiveFrameColor(), | |
| 291 InactiveFrameColor()); | |
| 292 } | |
| 293 | |
| 294 return custom_frame_view; | |
| 295 } | |
| 296 | |
| 297 return ChromeNativeAppWindowViews::CreateNonClientFrameView(widget); | |
| 298 } | |
| 299 | |
| 300 void ChromeNativeAppWindowViewsAura::SetFullscreen(int fullscreen_types) { | |
| 301 ChromeNativeAppWindowViews::SetFullscreen(fullscreen_types); | |
| 302 | |
| 303 if (immersive_fullscreen_controller_.get()) { | |
| 304 // |immersive_fullscreen_controller_| should only be set if immersive | |
| 305 // fullscreen is the fullscreen type used by the OS. | |
| 306 immersive_fullscreen_controller_->SetEnabled( | |
| 307 ash::ImmersiveFullscreenController::WINDOW_TYPE_PACKAGED_APP, | |
| 308 (fullscreen_types & AppWindow::FULLSCREEN_TYPE_OS) != 0); | |
| 309 // Autohide the shelf instead of hiding the shelf completely when only in | |
| 310 // OS fullscreen. | |
| 311 ash::wm::WindowState* window_state = | |
| 312 ash::wm::GetWindowState(widget()->GetNativeWindow()); | |
| 313 window_state->set_hide_shelf_when_fullscreen(fullscreen_types != | |
| 314 AppWindow::FULLSCREEN_TYPE_OS); | |
| 315 DCHECK(ash::Shell::HasInstance()); | |
| 316 ash::Shell::GetInstance()->UpdateShelfVisibility(); | |
| 317 } | |
| 318 } | |
| 319 | |
| 320 void ChromeNativeAppWindowViewsAura::UpdateShape(scoped_ptr<SkRegion> region) { | |
| 321 bool had_shape = shape() != nullptr; | |
| 322 | |
| 323 ChromeNativeAppWindowViews::UpdateShape(region.Pass()); | |
| 324 | |
| 325 aura::Window* native_window = widget()->GetNativeWindow(); | |
| 326 if (shape()) { | |
| 327 widget()->SetShape(new SkRegion(*shape())); | |
|
tapted
2015/02/25 05:54:35
these Widget::SetShape calls should be in the supe
jackhou1
2015/02/25 22:57:52
I don't think adding OnShapeChanged makes a big di
jackhou1
2015/02/25 22:57:52
Looks like it does. On Linux setting and unsetting
| |
| 328 if (!had_shape) { | |
| 329 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( | |
| 330 new ShapedAppWindowTargeter(native_window, this))); | |
| 331 } | |
| 332 } else { | |
| 333 widget()->SetShape(nullptr); | |
| 334 if (had_shape) | |
| 335 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>()); | |
| 336 } | |
| 337 } | |
| OLD | NEW |