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 |