Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(194)

Side by Side Diff: chrome/browser/ui/panels/panel_manager.cc

Issue 2263863002: Remove implementation of Panels on OSes other than ChromeOS. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: CR feedback Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/panels/panel_manager.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "build/build_config.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/ui/panels/detached_panel_collection.h"
16 #include "chrome/browser/ui/panels/docked_panel_collection.h"
17 #include "chrome/browser/ui/panels/panel_drag_controller.h"
18 #include "chrome/browser/ui/panels/panel_mouse_watcher.h"
19 #include "chrome/browser/ui/panels/panel_resize_controller.h"
20 #include "chrome/browser/ui/panels/stacked_panel_collection.h"
21 #include "chrome/common/channel_info.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "components/version_info/version_info.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/notification_source.h"
26 #include "extensions/common/constants.h"
27 #include "ui/base/hit_test.h"
28
29 #if defined(USE_X11) && !defined(OS_CHROMEOS)
30 #include "base/environment.h"
31 #include "base/nix/xdg_util.h"
32 #include "ui/base/x/x11_util.h"
33 #endif
34
35 namespace {
36 // Maxmium width of a panel is based on a factor of the working area.
37 #if defined(OS_CHROMEOS)
38 // ChromeOS device screens are relatively small and limiting the width
39 // interferes with some apps (e.g. http://crbug.com/111121).
40 const double kPanelMaxWidthFactor = 0.80;
41 #else
42 const double kPanelMaxWidthFactor = 0.35;
43 #endif
44
45 // Maxmium height of a panel is based on a factor of the working area.
46 const double kPanelMaxHeightFactor = 0.5;
47
48 // Width to height ratio is used to compute the default width or height
49 // when only one value is provided.
50 const double kPanelDefaultWidthToHeightRatio = 1.62; // golden ratio
51
52 // The test code could call PanelManager::SetDisplaySettingsProviderForTesting
53 // to set this for testing purpose.
54 DisplaySettingsProvider* display_settings_provider_for_testing;
55
56 // The following comparers are used by std::list<>::sort to determine which
57 // stack or panel we want to seacrh first for adding new panel.
58 bool ComparePanelsByPosition(Panel* panel1, Panel* panel2) {
59 gfx::Rect bounds1 = panel1->GetBounds();
60 gfx::Rect bounds2 = panel2->GetBounds();
61
62 // When there're ties, the right-most stack will appear first.
63 if (bounds1.x() > bounds2.x())
64 return true;
65 if (bounds1.x() < bounds2.x())
66 return false;
67
68 // In the event of another draw, the top-most stack will appear first.
69 return bounds1.y() < bounds2.y();
70 }
71
72 bool ComparerNumberOfPanelsInStack(StackedPanelCollection* stack1,
73 StackedPanelCollection* stack2) {
74 // The stack with more panels will appear first.
75 int num_panels_in_stack1 = stack1->num_panels();
76 int num_panels_in_stack2 = stack2->num_panels();
77 if (num_panels_in_stack1 > num_panels_in_stack2)
78 return true;
79 if (num_panels_in_stack1 < num_panels_in_stack2)
80 return false;
81
82 DCHECK(num_panels_in_stack1);
83
84 return ComparePanelsByPosition(stack1->top_panel(), stack2->top_panel());
85 }
86
87 bool CompareDetachedPanels(Panel* panel1, Panel* panel2) {
88 return ComparePanelsByPosition(panel1, panel2);
89 }
90
91 } // namespace
92
93 // static
94 bool PanelManager::shorten_time_intervals_ = false;
95
96 // static
97 PanelManager* PanelManager::GetInstance() {
98 static base::LazyInstance<PanelManager> instance = LAZY_INSTANCE_INITIALIZER;
99 return instance.Pointer();
100 }
101
102 // static
103 void PanelManager::SetDisplaySettingsProviderForTesting(
104 DisplaySettingsProvider* provider) {
105 display_settings_provider_for_testing = provider;
106 }
107
108 // static
109 bool PanelManager::ShouldUsePanels(const std::string& extension_id) {
110 // If --disable-panels is on, never use panels.
111 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
112 switches::kDisablePanels))
113 return false;
114
115 // If --enable-panels is on, always use panels.
116 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
117 switches::kEnablePanels))
118 return true;
119
120 #if defined(USE_X11) && !defined(OS_CHROMEOS)
121 // On Linux, panels are only supported on tested window managers.
122 ui::WindowManagerName wm_type = ui::GuessWindowManager();
123 if (wm_type != ui::WM_COMPIZ &&
124 wm_type != ui::WM_ICE_WM &&
125 wm_type != ui::WM_KWIN &&
126 wm_type != ui::WM_METACITY &&
127 wm_type != ui::WM_MUFFIN &&
128 wm_type != ui::WM_MUTTER &&
129 wm_type != ui::WM_XFWM4) {
130 return false;
131 }
132 #endif // USE_X11 && !OS_CHROMEOS
133
134 // Without --enable-panels, only support Hangouts.
135 for (const char* id : extension_misc::kHangoutsExtensionIds) {
136 if (extension_id == id)
137 return true;
138 }
139
140 return false;
141 }
142
143 // static
144 bool PanelManager::IsPanelStackingEnabled() {
145 // Stacked panel mode is not supported in linux-aura.
146 #if defined(OS_LINUX)
147 return false;
148 #else
149 return true;
150 #endif
151 }
152
153 // static
154 bool PanelManager::CanUseSystemMinimize() {
155 #if defined(USE_X11) && !defined(OS_CHROMEOS)
156 static base::nix::DesktopEnvironment desktop_env =
157 base::nix::DESKTOP_ENVIRONMENT_OTHER;
158 if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_OTHER) {
159 std::unique_ptr<base::Environment> env(base::Environment::Create());
160 desktop_env = base::nix::GetDesktopEnvironment(env.get());
161 }
162 return desktop_env != base::nix::DESKTOP_ENVIRONMENT_UNITY;
163 #else
164 return true;
165 #endif
166 }
167
168 PanelManager::PanelManager()
169 : panel_mouse_watcher_(PanelMouseWatcher::Create()),
170 auto_sizing_enabled_(true) {
171 // DisplaySettingsProvider should be created before the creation of
172 // collections since some collection might depend on it.
173 if (display_settings_provider_for_testing)
174 display_settings_provider_.reset(display_settings_provider_for_testing);
175 else
176 display_settings_provider_.reset(DisplaySettingsProvider::Create());
177 display_settings_provider_->AddDisplayObserver(this);
178
179 detached_collection_.reset(new DetachedPanelCollection(this));
180 docked_collection_.reset(new DockedPanelCollection(this));
181 drag_controller_.reset(new PanelDragController(this));
182 resize_controller_.reset(new PanelResizeController(this));
183 }
184
185 PanelManager::~PanelManager() {
186 display_settings_provider_->RemoveDisplayObserver(this);
187
188 // Docked collection should be disposed explicitly before
189 // DisplaySettingsProvider is gone since docked collection needs to remove
190 // the observer from DisplaySettingsProvider.
191 docked_collection_.reset();
192 }
193
194 gfx::Point PanelManager::GetDefaultDetachedPanelOrigin() {
195 return detached_collection_->GetDefaultPanelOrigin();
196 }
197
198 void PanelManager::OnDisplayChanged() {
199 docked_collection_->OnDisplayChanged();
200 detached_collection_->OnDisplayChanged();
201 for (Stacks::const_iterator iter = stacks_.begin();
202 iter != stacks_.end(); iter++)
203 (*iter)->OnDisplayChanged();
204 }
205
206 void PanelManager::OnFullScreenModeChanged(bool is_full_screen) {
207 std::vector<Panel*> all_panels = panels();
208 for (std::vector<Panel*>::const_iterator iter = all_panels.begin();
209 iter != all_panels.end(); ++iter) {
210 (*iter)->FullScreenModeChanged(is_full_screen);
211 }
212 }
213
214 int PanelManager::GetMaxPanelWidth(const gfx::Rect& work_area) const {
215 return static_cast<int>(work_area.width() * kPanelMaxWidthFactor);
216 }
217
218 int PanelManager::GetMaxPanelHeight(const gfx::Rect& work_area) const {
219 return static_cast<int>(work_area.height() * kPanelMaxHeightFactor);
220 }
221
222 Panel* PanelManager::CreatePanel(const std::string& app_name,
223 Profile* profile,
224 const GURL& url,
225 content::SiteInstance* source_site_instance,
226 const gfx::Rect& requested_bounds,
227 CreateMode mode) {
228 // Need to sync the display area if no panel is present. This is because:
229 // 1) Display area is not initialized until first panel is created.
230 // 2) On windows, display settings notification is tied to a window. When
231 // display settings are changed at the time that no panel exists, we do
232 // not receive any notification.
233 if (num_panels() == 0) {
234 display_settings_provider_->OnDisplaySettingsChanged();
235 display_settings_provider_->AddFullScreenObserver(this);
236 }
237
238 // Compute initial bounds for the panel.
239 int width = requested_bounds.width();
240 int height = requested_bounds.height();
241 if (width == 0)
242 width = height * kPanelDefaultWidthToHeightRatio;
243 else if (height == 0)
244 height = width / kPanelDefaultWidthToHeightRatio;
245
246 gfx::Rect work_area =
247 display_settings_provider_->GetWorkAreaMatching(requested_bounds);
248 gfx::Size min_size(panel::kPanelMinWidth, panel::kPanelMinHeight);
249 gfx::Size max_size(GetMaxPanelWidth(work_area), GetMaxPanelHeight(work_area));
250 if (width < min_size.width())
251 width = min_size.width();
252 else if (width > max_size.width())
253 width = max_size.width();
254
255 if (height < min_size.height())
256 height = min_size.height();
257 else if (height > max_size.height())
258 height = max_size.height();
259
260 // Create the panel.
261 Panel* panel = new Panel(profile, app_name, min_size, max_size);
262
263 // Find the appropriate panel collection to hold the new panel.
264 gfx::Rect adjusted_requested_bounds(
265 requested_bounds.x(), requested_bounds.y(), width, height);
266 PanelCollection::PositioningMask positioning_mask;
267 PanelCollection* collection = GetCollectionForNewPanel(
268 panel, adjusted_requested_bounds, mode, &positioning_mask);
269
270 // Let the panel collection decide the initial bounds.
271 gfx::Rect bounds = collection->GetInitialPanelBounds(
272 adjusted_requested_bounds);
273 bounds.AdjustToFit(work_area);
274
275 panel->Initialize(url, source_site_instance, bounds,
276 collection->UsesAlwaysOnTopPanels());
277
278 // Auto resizable feature is enabled only if no initial size is requested.
279 if (auto_sizing_enabled() && requested_bounds.width() == 0 &&
280 requested_bounds.height() == 0) {
281 panel->SetAutoResizable(true);
282 }
283
284 // Add the panel to the panel collection.
285 collection->AddPanel(panel, positioning_mask);
286 collection->UpdatePanelOnCollectionChange(panel);
287
288 return panel;
289 }
290
291 PanelCollection* PanelManager::GetCollectionForNewPanel(
292 Panel* new_panel,
293 const gfx::Rect& bounds,
294 CreateMode mode,
295 PanelCollection::PositioningMask* positioning_mask) {
296 if (mode == CREATE_AS_DOCKED) {
297 // Delay layout refreshes in case multiple panels are created within
298 // a short time of one another or the focus changes shortly after panel
299 // is created to avoid excessive screen redraws.
300 *positioning_mask = PanelCollection::DELAY_LAYOUT_REFRESH;
301 return docked_collection_.get();
302 }
303
304 DCHECK_EQ(CREATE_AS_DETACHED, mode);
305 *positioning_mask = PanelCollection::DEFAULT_POSITION;
306
307 // If the stacking support is not enabled, new panel will still be created as
308 // detached.
309 if (!IsPanelStackingEnabled())
310 return detached_collection_.get();
311
312 // If there're stacks, try to find a stack that can fit new panel.
313 if (!stacks_.empty()) {
314 // Perform the search as:
315 // 1) Search from the stack with more panels to the stack with least panels.
316 // 2) Amongs the stacks with same number of panels, search from the right-
317 // most stack to the left-most stack.
318 // 3) Among the stack with same number of panels and same x position,
319 // search from the top-most stack to the bottom-most stack.
320 // 4) If there is not enough space to fit new panel even with all inactive
321 // panels being collapsed, move to next stack.
322 stacks_.sort(ComparerNumberOfPanelsInStack);
323 for (Stacks::const_iterator iter = stacks_.begin();
324 iter != stacks_.end(); iter++) {
325 StackedPanelCollection* stack = *iter;
326
327 // Do not add to other stack that is from differnt extension or profile.
328 // Note that the check is based on bottom panel.
329 Panel* panel = stack->bottom_panel();
330 if (panel->profile() != new_panel->profile() ||
331 panel->extension_id() != new_panel->extension_id())
332 continue;
333
334 // Do not add to the stack that is minimized by the system.
335 if (stack->IsMinimized())
336 continue;
337
338 // Do not stack with the panel that is not shown in current virtual
339 // desktop.
340 if (!panel->IsShownOnActiveDesktop())
341 continue;
342
343 if (bounds.height() <= stack->GetMaximiumAvailableBottomSpace()) {
344 *positioning_mask = static_cast<PanelCollection::PositioningMask>(
345 *positioning_mask | PanelCollection::COLLAPSE_TO_FIT);
346 return stack;
347 }
348 }
349 }
350
351 // Then try to find a detached panel to which new panel can stack.
352 if (detached_collection_->num_panels()) {
353 // Perform the search as:
354 // 1) Search from the right-most detached panel to the left-most detached
355 // panel.
356 // 2) Among the detached panels with same x position, search from the
357 // top-most detached panel to the bottom-most deatched panel.
358 // 3) If there is not enough space beneath the detached panel, even by
359 // collapsing it if it is inactive, to fit new panel, move to next
360 // detached panel.
361 detached_collection_->SortPanels(CompareDetachedPanels);
362
363 for (DetachedPanelCollection::Panels::const_iterator iter =
364 detached_collection_->panels().begin();
365 iter != detached_collection_->panels().end(); ++iter) {
366 Panel* panel = *iter;
367
368 // Do not stack with other panel that is from differnt extension or
369 // profile.
370 if (panel->profile() != new_panel->profile() ||
371 panel->extension_id() != new_panel->extension_id())
372 continue;
373
374 // Do not stack with the panel that is minimized by the system.
375 if (panel->IsMinimizedBySystem())
376 continue;
377
378 // Do not stack with the panel that is not shown in the active desktop.
379 if (!panel->IsShownOnActiveDesktop())
380 continue;
381
382 gfx::Rect work_area =
383 display_settings_provider_->GetWorkAreaMatching(panel->GetBounds());
384 int max_available_space =
385 work_area.bottom() - panel->GetBounds().y() -
386 (panel->IsActive() ? panel->GetBounds().height()
387 : panel::kTitlebarHeight);
388 if (bounds.height() <= max_available_space) {
389 StackedPanelCollection* new_stack = CreateStack();
390 MovePanelToCollection(panel,
391 new_stack,
392 PanelCollection::DEFAULT_POSITION);
393 *positioning_mask = static_cast<PanelCollection::PositioningMask>(
394 *positioning_mask | PanelCollection::COLLAPSE_TO_FIT);
395 return new_stack;
396 }
397 }
398 }
399
400 return detached_collection_.get();
401 }
402
403 void PanelManager::OnPanelClosed(Panel* panel) {
404 if (num_panels() == 1) {
405 display_settings_provider_->RemoveFullScreenObserver(this);
406 }
407
408 drag_controller_->OnPanelClosed(panel);
409 resize_controller_->OnPanelClosed(panel);
410
411 // Note that we need to keep track of panel's collection since it will be
412 // gone once RemovePanel is called.
413 PanelCollection* collection = panel->collection();
414 collection->RemovePanel(panel, PanelCollection::PANEL_CLOSED);
415
416 // If only one panel is left in the stack, move it out of the stack.
417 // Also make sure that this detached panel will be expanded if not yet.
418 if (collection->type() == PanelCollection::STACKED) {
419 StackedPanelCollection* stack =
420 static_cast<StackedPanelCollection*>(collection);
421 DCHECK_GE(stack->num_panels(), 1);
422 if (stack->num_panels() == 1) {
423 Panel* top_panel = stack->top_panel();
424 MovePanelToCollection(top_panel,
425 detached_collection(),
426 PanelCollection::DEFAULT_POSITION);
427 if (top_panel->expansion_state() != Panel::EXPANDED)
428 top_panel->SetExpansionState(Panel::EXPANDED);
429 RemoveStack(stack);
430 }
431 }
432
433 content::NotificationService::current()->Notify(
434 chrome::NOTIFICATION_PANEL_CLOSED,
435 content::Source<Panel>(panel),
436 content::NotificationService::NoDetails());
437 }
438
439 StackedPanelCollection* PanelManager::CreateStack() {
440 StackedPanelCollection* stack = new StackedPanelCollection(this);
441 stacks_.push_back(stack);
442 return stack;
443 }
444
445 void PanelManager::RemoveStack(StackedPanelCollection* stack) {
446 DCHECK_EQ(0, stack->num_panels());
447 stacks_.remove(stack);
448 stack->CloseAll();
449 delete stack;
450 }
451
452 void PanelManager::StartDragging(Panel* panel,
453 const gfx::Point& mouse_location) {
454 drag_controller_->StartDragging(panel, mouse_location);
455 }
456
457 void PanelManager::Drag(const gfx::Point& mouse_location) {
458 drag_controller_->Drag(mouse_location);
459 }
460
461 void PanelManager::EndDragging(bool cancelled) {
462 drag_controller_->EndDragging(cancelled);
463 }
464
465 void PanelManager::StartResizingByMouse(Panel* panel,
466 const gfx::Point& mouse_location,
467 int component) {
468 if (panel->CanResizeByMouse() != panel::NOT_RESIZABLE &&
469 component != HTNOWHERE) {
470 resize_controller_->StartResizing(panel, mouse_location, component);
471 }
472 }
473
474 void PanelManager::ResizeByMouse(const gfx::Point& mouse_location) {
475 if (resize_controller_->IsResizing())
476 resize_controller_->Resize(mouse_location);
477 }
478
479 void PanelManager::EndResizingByMouse(bool cancelled) {
480 if (resize_controller_->IsResizing()) {
481 Panel* resized_panel = resize_controller_->EndResizing(cancelled);
482 if (!cancelled && resized_panel->collection())
483 resized_panel->collection()->RefreshLayout();
484 }
485 }
486
487 void PanelManager::OnPanelExpansionStateChanged(Panel* panel) {
488 panel->collection()->OnPanelExpansionStateChanged(panel);
489 }
490
491 void PanelManager::MovePanelToCollection(
492 Panel* panel,
493 PanelCollection* target_collection,
494 PanelCollection::PositioningMask positioning_mask) {
495 DCHECK(panel);
496 PanelCollection* current_collection = panel->collection();
497 DCHECK(current_collection);
498 DCHECK_NE(current_collection, target_collection);
499 current_collection->RemovePanel(panel,
500 PanelCollection::PANEL_CHANGED_COLLECTION);
501
502 target_collection->AddPanel(panel, positioning_mask);
503 target_collection->UpdatePanelOnCollectionChange(panel);
504 panel->SetAlwaysOnTop(target_collection->UsesAlwaysOnTopPanels());
505 }
506
507 bool PanelManager::ShouldBringUpTitlebars(int mouse_x, int mouse_y) const {
508 return docked_collection_->ShouldBringUpTitlebars(mouse_x, mouse_y);
509 }
510
511 void PanelManager::BringUpOrDownTitlebars(bool bring_up) {
512 docked_collection_->BringUpOrDownTitlebars(bring_up);
513 }
514
515 void PanelManager::CloseAll() {
516 DCHECK(!drag_controller_->is_dragging());
517
518 detached_collection_->CloseAll();
519 docked_collection_->CloseAll();
520 }
521
522 int PanelManager::num_panels() const {
523 int count = detached_collection_->num_panels() +
524 docked_collection_->num_panels();
525 for (Stacks::const_iterator iter = stacks_.begin();
526 iter != stacks_.end(); iter++)
527 count += (*iter)->num_panels();
528 return count;
529 }
530
531 std::vector<Panel*> PanelManager::panels() const {
532 std::vector<Panel*> panels;
533 for (DetachedPanelCollection::Panels::const_iterator iter =
534 detached_collection_->panels().begin();
535 iter != detached_collection_->panels().end(); ++iter)
536 panels.push_back(*iter);
537 for (DockedPanelCollection::Panels::const_iterator iter =
538 docked_collection_->panels().begin();
539 iter != docked_collection_->panels().end(); ++iter)
540 panels.push_back(*iter);
541 for (Stacks::const_iterator stack_iter = stacks_.begin();
542 stack_iter != stacks_.end(); stack_iter++) {
543 for (StackedPanelCollection::Panels::const_iterator iter =
544 (*stack_iter)->panels().begin();
545 iter != (*stack_iter)->panels().end(); ++iter) {
546 panels.push_back(*iter);
547 }
548 }
549 return panels;
550 }
551
552 std::vector<Panel*> PanelManager::GetDetachedAndStackedPanels() const {
553 std::vector<Panel*> panels;
554 for (DetachedPanelCollection::Panels::const_iterator iter =
555 detached_collection_->panels().begin();
556 iter != detached_collection_->panels().end(); ++iter)
557 panels.push_back(*iter);
558 for (Stacks::const_iterator stack_iter = stacks_.begin();
559 stack_iter != stacks_.end(); stack_iter++) {
560 for (StackedPanelCollection::Panels::const_iterator iter =
561 (*stack_iter)->panels().begin();
562 iter != (*stack_iter)->panels().end(); ++iter) {
563 panels.push_back(*iter);
564 }
565 }
566 return panels;
567 }
568
569 void PanelManager::SetMouseWatcher(PanelMouseWatcher* watcher) {
570 panel_mouse_watcher_.reset(watcher);
571 }
572
573 void PanelManager::OnPanelAnimationEnded(Panel* panel) {
574 content::NotificationService::current()->Notify(
575 chrome::NOTIFICATION_PANEL_BOUNDS_ANIMATIONS_FINISHED,
576 content::Source<Panel>(panel),
577 content::NotificationService::NoDetails());
578 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/panels/panel_manager.h ('k') | chrome/browser/ui/panels/panel_mouse_watcher.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698