| // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "window_manager/panel_bar.h" |
| |
| #include <algorithm> |
| |
| #include <gflags/gflags.h> |
| |
| #include "base/logging.h" |
| #include "window_manager/clutter_interface.h" |
| #include "window_manager/panel.h" |
| #include "window_manager/shadow.h" |
| #include "window_manager/stacking_manager.h" |
| #include "window_manager/window.h" |
| #include "window_manager/window_manager.h" |
| #include "window_manager/x_connection.h" |
| |
| DEFINE_string(panel_bar_image, "../assets/images/panel_bar_bg.png", |
| "Image to use for the panel bar's background"); |
| DEFINE_string(panel_anchor_image, "../assets/images/panel_anchor.png", |
| "Image to use for anchors on the panel bar"); |
| |
| namespace chromeos { |
| |
| // Amount of padding to place between titlebars in the panel bar. |
| static const int kBarPadding = 1; |
| |
| // Amount of time to take for animations. |
| static const int kAnimMs = 150; |
| |
| // Frequency with which we should update the position of dragged panels. |
| static const int kDraggedPanelUpdateMs = 25; |
| |
| PanelBar::PanelBar(WindowManager* wm, int x, int y, int width, int height) |
| : wm_(wm), |
| x_(x), |
| y_(y), |
| width_(width), |
| height_(height), |
| collapsed_panel_width_(0), |
| bar_actor_(wm_->clutter()->CreateImage(FLAGS_panel_bar_image)), |
| bar_shadow_(new Shadow(wm_->clutter())), |
| dragged_panel_(NULL), |
| dragged_panel_event_coalescer_( |
| NewPermanentCallback(this, &PanelBar::MoveDraggedPanel), |
| kDraggedPanelUpdateMs), |
| anchor_input_win_(None), |
| anchor_panel_(NULL), |
| anchor_actor_(wm_->clutter()->CreateImage(FLAGS_panel_anchor_image)), |
| desired_panel_to_focus_(NULL), |
| is_visible_(false), |
| saw_map_request_(false) { |
| bar_actor_->SetVisibility(false); |
| wm_->stage()->AddActor(bar_actor_.get()); |
| wm_->stacking_manager()->StackActorAtTopOfLayer( |
| bar_actor_.get(), StackingManager::LAYER_PANEL_BAR); |
| bar_actor_->SetName("panel bar"); |
| bar_actor_->SetSize(width_, height_); |
| bar_actor_->Move(x_, y_ + height, 0); |
| bar_actor_->SetVisibility(true); |
| |
| bar_shadow_->group()->SetName("shadow group for panel bar"); |
| bar_shadow_->SetOpacity(0, 0); |
| wm_->stage()->AddActor(bar_shadow_->group()); |
| wm_->stacking_manager()->StackActorAtTopOfLayer( |
| bar_shadow_->group(), StackingManager::LAYER_PANEL_BAR); |
| bar_shadow_->Move(x_, y_ + height, 0); |
| bar_shadow_->Resize(width_, height_, 0); |
| bar_shadow_->Show(); |
| |
| anchor_actor_->SetName("panel anchor"); |
| anchor_actor_->SetOpacity(0, 0); |
| wm_->stage()->AddActor(anchor_actor_.get()); |
| wm_->stacking_manager()->StackActorAtTopOfLayer( |
| anchor_actor_.get(), StackingManager::LAYER_PANEL_BAR); |
| } |
| |
| PanelBar::~PanelBar() { |
| } |
| |
| bool PanelBar::HandleWindowMapRequest(Window* win) { |
| saw_map_request_ = true; |
| |
| if (win->type() != WmIpc::WINDOW_TYPE_CHROME_PANEL && |
| win->type() != WmIpc::WINDOW_TYPE_CHROME_PANEL_TITLEBAR) |
| return false; |
| |
| DoInitialSetupForWindow(win); |
| win->MapClient(); |
| return true; |
| } |
| |
| void PanelBar::HandleWindowMap(Window* win) { |
| CHECK(win); |
| |
| if (win->type() != WmIpc::WINDOW_TYPE_CHROME_PANEL && |
| win->type() != WmIpc::WINDOW_TYPE_CHROME_PANEL_TITLEBAR) |
| return; |
| |
| // Handle initial setup for existing windows for which we never saw a map |
| // request event. |
| if (!saw_map_request_) |
| DoInitialSetupForWindow(win); |
| |
| switch (win->type()) { |
| case WmIpc::WINDOW_TYPE_CHROME_PANEL_TITLEBAR: |
| // Don't do anything with panel titlebars when they're first |
| // mapped; we'll handle them after we see the corresponding panel. |
| break; |
| case WmIpc::WINDOW_TYPE_CHROME_PANEL: { |
| if (win->type_params().empty()) { |
| LOG(WARNING) << "Panel " << win->xid_str() << " is missing type " |
| << "parameter for titlebar window"; |
| break; |
| } |
| Window* titlebar = wm_->GetWindow(win->type_params().at(0)); |
| if (!titlebar) { |
| LOG(WARNING) << "Unable to find titlebar " |
| << XidStr(win->type_params()[0]) |
| << " for panel " << win->xid_str(); |
| break; |
| } |
| |
| // TODO(derat): Make the second param required after Chrome has been |
| // updated. |
| bool expanded = win->type_params().size() >= 2 ? |
| win->type_params().at(1) : false; |
| AddPanel(win, titlebar, expanded); |
| break; |
| } |
| default: |
| NOTREACHED() << "Unhandled window type " << win->type(); |
| } |
| |
| if (num_panels() > 0 && !is_visible_) { |
| SetVisibility(true); |
| } |
| } |
| |
| void PanelBar::HandleWindowUnmap(Window* win) { |
| CHECK(win); |
| Panel* panel = GetPanelByWindow(*win); |
| if (!panel) { |
| return; |
| } |
| |
| std::vector<XWindow> input_windows; |
| panel->GetInputWindows(&input_windows); |
| for (std::vector<XWindow>::const_iterator it = input_windows.begin(); |
| it != input_windows.end(); ++it) { |
| CHECK(panel_input_windows_.erase(*it) == 1); |
| } |
| |
| if (dragged_panel_ == panel) { |
| HandlePanelDragComplete(win); |
| } |
| if (anchor_panel_ == panel) { |
| DestroyAnchor(); |
| } |
| if (desired_panel_to_focus_ == panel) { |
| desired_panel_to_focus_ = NULL; |
| } |
| |
| // If this was a focused panel window, then we need to give the focus |
| // away to someone else. |
| if (panel->panel_win()->focused()) { |
| // Try to find another expanded panel to focus. |
| Panel* panel_to_focus = GetNearestExpandedPanel(panel); |
| if (panel_to_focus) { |
| FocusPanel(panel_to_focus, false); // remove_pointer_grab=false |
| } else { |
| // Failing that, let the WindowManager decide what to do with it. |
| wm_->SetActiveWindowProperty(None); |
| wm_->TakeFocus(); |
| } |
| } |
| |
| Panels::iterator it = FindPanelInVectorByWindow(collapsed_panels_, *win); |
| if (it != collapsed_panels_.end()) { |
| collapsed_panel_width_ -= ((*it)->titlebar_width() + kBarPadding); |
| collapsed_panels_.erase(it); |
| PackCollapsedPanels(); |
| } else { |
| it = FindPanelInVectorByWindow(expanded_panels_, *win); |
| if (it != expanded_panels_.end()) { |
| expanded_panels_.erase(it); |
| } else { |
| LOG(WARNING) << "Got panel " << panel->xid_str() << " for window " |
| << win->xid_str() << " but didn't find it in " |
| << "collapsed_panels_ or expanded_panels_"; |
| } |
| } |
| |
| if (num_panels() == 0 && is_visible_) { |
| SetVisibility(false); |
| } |
| } |
| |
| bool PanelBar::HandleWindowConfigureRequest( |
| Window* win, int req_x, int req_y, int req_width, int req_height) { |
| Panel* panel = GetPanelByWindow(*win); |
| if (!panel) |
| return false; |
| |
| // Ignore the request (we'll get strange behavior if we honor a resize |
| // request from the client while the user is manually resizing the |
| // panel). |
| // TODO: This means that panels can't resize themselves, which isn't what |
| // we want. If the user is currently resizing the window, we might want |
| // to save the panel's resize request and apply it afterwards. |
| return true; |
| } |
| |
| bool PanelBar::HandleButtonPress( |
| XWindow xid, int x, int y, int button, Time timestamp) { |
| // If the press was in the anchor window, destroy the anchor and |
| // collapse the corresponding panel. |
| if (xid == anchor_input_win_) { |
| if (button != 1) { |
| return true; |
| } |
| VLOG(1) << "Got button press in anchor window"; |
| Panel* panel = anchor_panel_; |
| DestroyAnchor(); |
| if (panel) { |
| CollapsePanel(panel); |
| } else { |
| LOG(WARNING) << "Anchor panel no longer exists"; |
| } |
| return true; |
| } |
| |
| std::map<XWindow, Panel*>::iterator it = panel_input_windows_.find(xid); |
| if (it != panel_input_windows_.end()) { |
| it->second->HandleInputWindowButtonPress(xid, x, y, button, timestamp); |
| return true; |
| } |
| |
| // Otherwise, check if this was in a panel window whose button presses |
| // we've grabbed. If so, give the focus to the panel. |
| Window* win = wm_->GetWindow(xid); |
| if (win) { |
| Panel* panel = GetPanelByWindow(*win); |
| if (panel) { |
| if (win == panel->panel_win()) { |
| VLOG(1) << "Got button press in panel " << panel->xid_str() |
| << "; giving it the focus"; |
| // Get rid of the passive button grab, and then ungrab the pointer |
| // and replay events so the panel will get a copy of the click. |
| FocusPanel(panel, true); // remove_pointer_grab=true |
| } |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool PanelBar::HandleButtonRelease( |
| XWindow xid, int x, int y, int button, Time timestamp) { |
| std::map<XWindow, Panel*>::iterator it = panel_input_windows_.find(xid); |
| if (it != panel_input_windows_.end()) { |
| it->second->HandleInputWindowButtonRelease(xid, x, y, button); |
| return true; |
| } |
| return false; |
| } |
| |
| bool PanelBar::HandlePointerLeave(XWindow xid, Time timestamp) { |
| // TODO: There appears to be a bit of a race condition here. If the |
| // mouse cursor has already been moved away before the anchor input |
| // window gets created, the anchor never gets a mouse leave event. Find |
| // some way to work around this. |
| if (xid != anchor_input_win_) { |
| return false; |
| } |
| VLOG(1) << "Got mouse leave in anchor window"; |
| DestroyAnchor(); |
| return true; |
| } |
| |
| bool PanelBar::HandlePointerMotion(XWindow xid, int x, int y, Time timestamp) { |
| std::map<XWindow, Panel*>::iterator it = panel_input_windows_.find(xid); |
| if (it != panel_input_windows_.end()) { |
| it->second->HandleInputWindowPointerMotion(xid, x, y); |
| return true; |
| } |
| return false; |
| } |
| |
| bool PanelBar::HandleChromeMessage(const WmIpc::Message& msg) { |
| switch (msg.type()) { |
| // TODO: This is getting long; move cases into individual methods. |
| case WmIpc::Message::WM_SET_PANEL_STATE: { |
| XWindow xid = msg.param(0); |
| Window* win = wm_->GetWindow(xid); |
| if (!win) { |
| LOG(WARNING) << "Ignoring WM_SET_PANEL_STATE message for unknown " |
| << "window " << XidStr(xid); |
| return true; |
| } |
| Panel* panel = GetPanelByWindow(*win); |
| if (!panel) { |
| LOG(WARNING) << "Ignoring WM_SET_PANEL_STATE message for non-panel " |
| << "window " << win->xid_str(); |
| return true; |
| } |
| if (msg.param(1)) { |
| ExpandPanel(panel, true); // create_anchor |
| } else { |
| CollapsePanel(panel); |
| } |
| break; |
| } |
| case WmIpc::Message::WM_MOVE_PANEL: { |
| XWindow xid = msg.param(0); |
| Window* win = wm_->GetWindow(xid); |
| if (!win) { |
| LOG(WARNING) << "Ignoring WM_MOVE_PANEL message for unknown window " |
| << XidStr(xid); |
| return true; |
| } |
| int x = msg.param(1); |
| int y = msg.param(2); |
| StorePanelPosition(win, x, y); |
| break; |
| } |
| case WmIpc::Message::WM_NOTIFY_PANEL_DRAG_COMPLETE: { |
| XWindow xid = msg.param(0); |
| Window* win = wm_->GetWindow(xid); |
| if (!win) { |
| LOG(WARNING) << "Ignoring WM_NOTIFY_PANEL_DRAG_COMPLETE message for " |
| << "unknown window " << XidStr(xid); |
| return true; |
| } |
| HandlePanelDragComplete(win); |
| break; |
| } |
| case WmIpc::Message::WM_FOCUS_WINDOW: { |
| XWindow xid = msg.param(0); |
| Window* win = wm_->GetWindow(xid); |
| if (!win) { |
| LOG(WARNING) << "Got WM_FOCUS_WINDOW message for unknown window " |
| << XidStr(xid); |
| return false; |
| } |
| Panel* panel = GetPanelByWindow(*win); |
| if (!panel) { |
| // Not a panel -- maybe it's a top-level window. |
| return false; |
| } |
| if (!panel->is_expanded()) { |
| LOG(WARNING) << "Ignoring WM_FOCUS_WINDOW message for collapsed panel " |
| << panel->xid_str(); |
| return true; |
| } |
| FocusPanel(panel, false); // remove_pointer_grab=false |
| break; |
| } |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| bool PanelBar::HandleClientMessage(const XClientMessageEvent& e) { |
| Window* win = wm_->GetWindow(e.window); |
| if (!win) |
| return false; |
| |
| Panel* panel = GetPanelByWindow(*win); |
| if (!panel) |
| return false; |
| |
| if (e.message_type == wm_->GetXAtom(ATOM_NET_ACTIVE_WINDOW)) { |
| if (e.format != XConnection::kLongFormat) |
| return true; |
| VLOG(1) << "Got _NET_ACTIVE_WINDOW request to focus " << XidStr(e.window) |
| << " (requestor says its currently-active window is " |
| << XidStr(e.data.l[2]) << "; real active window is " |
| << XidStr(wm_->active_window_xid()) << ")"; |
| |
| if (!panel->is_expanded()) |
| ExpandPanel(panel, false); // create_anchor=false |
| FocusPanel(panel, false); // remove_pointer_grab=false |
| return true; |
| } |
| return false; |
| } |
| |
| bool PanelBar::HandleFocusChange(XWindow xid, bool focus_in) { |
| Window* win = wm_->GetWindow(xid); |
| if (!win) { |
| return false; |
| } |
| Panel* panel = GetPanelByWindow(*win); |
| if (!panel) { |
| return false; |
| } |
| |
| if (!focus_in) { |
| VLOG(1) << "Panel " << panel->xid_str() << " lost focus; adding passive " |
| << "button grab"; |
| panel->panel_win()->AddPassiveButtonGrab(); |
| } |
| return true; |
| } |
| |
| void PanelBar::MoveAndResize(int x, int y, int width, int height) { |
| x_ = x; |
| y_ = y; |
| width_ = width; |
| height_ = height; |
| |
| bar_actor_->SetSize(width_, height_); |
| bar_actor_->Move(x_, y_ + (is_visible_ ? 0 : height_), 0); |
| bar_shadow_->Resize(width_, height_, 0); |
| bar_shadow_->Move(x_, y_ + (is_visible_ ? 0 : height_), 0); |
| |
| // Update all of the panels' Y positions... |
| for (Panels::iterator it = expanded_panels_.begin(); |
| it != expanded_panels_.end(); ++it) { |
| (*it)->HandlePanelBarMove(); |
| } |
| for (Panels::iterator it = collapsed_panels_.begin(); |
| it != collapsed_panels_.end(); ++it) { |
| (*it)->HandlePanelBarMove(); |
| } |
| |
| // ... and their X positions. |
| PackCollapsedPanels(); |
| if (!expanded_panels_.empty()) |
| RepositionExpandedPanels(expanded_panels_[0].get()); |
| } |
| |
| void PanelBar::StorePanelPosition(Window* win, int x, int y) { |
| CHECK(win); |
| VLOG(2) << "Got request to move panel " << win->xid_str() |
| << " to (" << x << ", " << y << ")"; |
| |
| dragged_panel_event_coalescer_.StorePosition(x, y); |
| |
| if (!dragged_panel_ || dragged_panel_->panel_win() != win) { |
| Panel* panel = GetPanelByWindow(*win); |
| if (!panel) { |
| LOG(WARNING) << "Unable to store position for unknown panel " |
| << win->xid_str(); |
| return; |
| } |
| StartDrag(panel); |
| } |
| } |
| |
| void PanelBar::HandlePanelDragComplete(Window* win) { |
| CHECK(win); |
| VLOG(2) << "Got notification that panel drag is complete for " |
| << win->xid_str(); |
| |
| if (!dragged_panel_ || dragged_panel_->panel_win() != win) { |
| return; |
| } |
| |
| if (dragged_panel_->is_expanded()) { |
| dragged_panel_->StackAtTopOfLayer(StackingManager::LAYER_EXPANDED_PANEL); |
| RepositionExpandedPanels(dragged_panel_); |
| } else { |
| dragged_panel_->StackAtTopOfLayer(StackingManager::LAYER_COLLAPSED_PANEL); |
| // Snap collapsed dragged panels to their correct position. |
| dragged_panel_->Move(dragged_panel_->snapped_right(), kAnimMs); |
| } |
| dragged_panel_ = NULL; |
| |
| if (dragged_panel_event_coalescer_.IsRunning()) { |
| dragged_panel_event_coalescer_.Stop(); |
| } |
| } |
| |
| void PanelBar::MoveDraggedPanel() { |
| if (!dragged_panel_) { |
| return; |
| } |
| |
| const int drag_x = dragged_panel_event_coalescer_.x(); |
| dragged_panel_->Move(drag_x + dragged_panel_->titlebar_width(), 0); |
| |
| // When an expanded panel is being dragged, we don't move the other |
| // panels to make room for it until the drag is done. |
| if (dragged_panel_->is_expanded()) { |
| return; |
| } |
| |
| // For collapsed panels, we first find the position of the dragged panel. |
| Panels::iterator dragged_it = FindPanelInVectorByWindow( |
| collapsed_panels_, *(dragged_panel_->panel_win())); |
| CHECK(dragged_it != collapsed_panels_.end()); |
| |
| // Next, check if the center of the panel has moved over another panel. |
| const int center_x = drag_x + 0.5 * dragged_panel_->titlebar_width(); |
| Panels::iterator it = find_if(collapsed_panels_.begin(), |
| collapsed_panels_.end(), |
| PanelTitlebarContainsPoint(center_x)); |
| |
| // If it has, then we reorder the panels. |
| if (it != collapsed_panels_.end() && it->get() != dragged_panel_) { |
| if (it > dragged_it) { |
| rotate(dragged_it, dragged_it + 1, it + 1); |
| } else { |
| rotate(it, dragged_it, dragged_it + 1); |
| } |
| PackCollapsedPanels(); |
| } |
| } |
| |
| bool PanelBar::TakeFocus() { |
| // If we already decided on a panel to focus, use it. |
| if (desired_panel_to_focus_) { |
| FocusPanel(desired_panel_to_focus_, false); // remove_pointer_grab=false |
| return true; |
| } |
| |
| // Just focus the first expanded panel. |
| if (expanded_panels_.empty()) { |
| return false; |
| } |
| FocusPanel(expanded_panels_[0].get(), false); // remove_pointer_grab=false |
| return true; |
| } |
| |
| |
| bool PanelBar::PanelTitlebarContainsPoint::operator()( |
| std::tr1::shared_ptr<Panel>& p) { |
| return (center_x_ >= p->snapped_titlebar_left() && |
| center_x_ < p->snapped_right()); |
| } |
| |
| |
| void PanelBar::DoInitialSetupForWindow(Window* win) { |
| wm_->stacking_manager()->StackWindowAtTopOfLayer( |
| win, StackingManager::LAYER_COLLAPSED_PANEL); |
| win->MoveClientOffscreen(); |
| } |
| |
| void PanelBar::AddPanel( |
| Window* panel_win, Window* titlebar_win, bool expanded) { |
| CHECK(panel_win); |
| CHECK(titlebar_win); |
| |
| VLOG(1) << "Adding " << (expanded ? "expanded" : "collapsed") |
| << " panel with panel window " << panel_win->xid_str() |
| << " and titlebar window " << titlebar_win->xid_str(); |
| |
| const int right = expanded ? |
| x_ + width_ - kBarPadding : |
| x_ + width_ - collapsed_panel_width_ - kBarPadding; |
| std::tr1::shared_ptr<Panel> panel( |
| new Panel(this, panel_win, titlebar_win, right)); |
| |
| std::vector<XWindow> input_windows; |
| panel->GetInputWindows(&input_windows); |
| for (std::vector<XWindow>::const_iterator it = input_windows.begin(); |
| it != input_windows.end(); ++it) { |
| CHECK(panel_input_windows_.insert(std::make_pair(*it, panel.get())).second); |
| } |
| |
| if (!expanded) { |
| panel->StackAtTopOfLayer(StackingManager::LAYER_COLLAPSED_PANEL); |
| collapsed_panels_.insert(collapsed_panels_.begin(), panel); |
| collapsed_panel_width_ += panel->titlebar_width() + kBarPadding; |
| } else { |
| panel->StackAtTopOfLayer(StackingManager::LAYER_EXPANDED_PANEL); |
| collapsed_panels_.push_back(panel); |
| ExpandPanel(panel.get(), false); // create_anchor |
| } |
| } |
| |
| void PanelBar::ExpandPanel(Panel* panel, bool create_anchor) { |
| CHECK(panel); |
| if (panel->is_expanded()) { |
| LOG(WARNING) << "Ignoring request to expand already-expanded panel " |
| << panel->xid_str(); |
| return; |
| } |
| |
| Panels::iterator it = |
| FindPanelInVectorByWindow(collapsed_panels_, *(panel->panel_win())); |
| CHECK(it != collapsed_panels_.end()); |
| std::tr1::shared_ptr<Panel> ref = *it; |
| |
| if (create_anchor) { |
| CreateAnchor(panel); |
| } |
| panel->SetState(true); |
| collapsed_panels_.erase(it); |
| InsertExpandedPanel(ref); |
| RepositionExpandedPanels(panel); |
| } |
| |
| void PanelBar::CollapsePanel(Panel* panel) { |
| CHECK(panel); |
| if (!panel->is_expanded()) { |
| LOG(WARNING) << "Ignoring request to collapse already-collapsed panel " |
| << panel->xid_str(); |
| return; |
| } |
| |
| // In case we need to focus another panel, find the nearest one before we |
| // collapse this one. |
| Panel* panel_to_focus = GetNearestExpandedPanel(panel); |
| |
| // Move the panel from 'expanded_panels_' to 'collapsed_panels_'. |
| Panels::iterator it = |
| FindPanelInVectorByWindow(expanded_panels_, *(panel->panel_win())); |
| CHECK(it != expanded_panels_.end()); |
| std::tr1::shared_ptr<Panel> ref = *it; |
| |
| if (anchor_panel_ == panel) { |
| DestroyAnchor(); |
| } |
| |
| panel->SetState(false); |
| expanded_panels_.erase(it); |
| InsertCollapsedPanel(std::tr1::shared_ptr<Panel>(ref)); |
| PackCollapsedPanels(); |
| |
| // Give up the focus if this panel had it. |
| if (panel->panel_win()->focused()) { |
| desired_panel_to_focus_ = panel_to_focus; |
| if (!TakeFocus()) { |
| wm_->SetActiveWindowProperty(None); |
| wm_->TakeFocus(); |
| } |
| } |
| } |
| |
| void PanelBar::FocusPanel(Panel* panel, bool remove_pointer_grab) { |
| CHECK(panel); |
| panel->panel_win()->RemovePassiveButtonGrab(); |
| if (remove_pointer_grab) { |
| wm_->xconn()->RemoveActivePointerGrab(true); // replay_events |
| } |
| wm_->SetActiveWindowProperty(panel->panel_win()->xid()); |
| panel->panel_win()->TakeFocus(wm_->GetCurrentTimeFromServer()); |
| panel->StackAtTopOfLayer(StackingManager::LAYER_EXPANDED_PANEL); |
| desired_panel_to_focus_ = panel; |
| } |
| |
| Panel* PanelBar::GetPanelByWindow(const Window& win) { |
| Panels::iterator it = FindPanelInVectorByWindow(collapsed_panels_, win); |
| if (it != collapsed_panels_.end()) { |
| return it->get(); |
| } |
| |
| it = FindPanelInVectorByWindow(expanded_panels_, win); |
| if (it != expanded_panels_.end()) { |
| return it->get(); |
| } |
| |
| return NULL; |
| } |
| |
| PanelBar::Panels::iterator PanelBar::FindPanelInVectorByWindow( |
| Panels& panels, const Window& win) { |
| for (Panels::iterator it = panels.begin(); it != panels.end(); ++it) { |
| if ((*it)->titlebar_win() == &win || (*it)->panel_win() == &win) { |
| return it; |
| } |
| } |
| return panels.end(); |
| } |
| |
| int PanelBar::GetPanelIndex(Panels& panels, const Panel& panel) { |
| Panels::iterator it = |
| FindPanelInVectorByWindow(panels, *(panel.const_panel_win())); |
| if (it == panels.end()) { |
| return -1; |
| } |
| return it - panels.begin(); |
| } |
| |
| void PanelBar::StartDrag(Panel* panel) { |
| if (dragged_panel_ == panel) { |
| return; |
| } |
| |
| if (dragged_panel_) { |
| LOG(WARNING) << "Abandoning dragged panel " << dragged_panel_->xid_str() |
| << " in favor of " << panel->xid_str(); |
| if (dragged_panel_->is_expanded()) { |
| RepositionExpandedPanels(dragged_panel_); |
| } |
| } |
| |
| VLOG(2) << "Starting drag of panel " << panel->xid_str(); |
| dragged_panel_ = panel; |
| |
| panel->StackAtTopOfLayer( |
| panel->is_expanded() ? |
| StackingManager::LAYER_DRAGGED_EXPANDED_PANEL : |
| StackingManager::LAYER_DRAGGED_COLLAPSED_PANEL); |
| |
| if (!dragged_panel_event_coalescer_.IsRunning()) |
| dragged_panel_event_coalescer_.Start(); |
| } |
| |
| void PanelBar::PackCollapsedPanels() { |
| collapsed_panel_width_ = 0; |
| |
| for (Panels::reverse_iterator it = collapsed_panels_.rbegin(); |
| it != collapsed_panels_.rend(); ++it) { |
| Panel* panel = it->get(); |
| |
| int right = x_ + width_ - collapsed_panel_width_ - kBarPadding; |
| panel->set_snapped_right(right); |
| collapsed_panel_width_ += panel->titlebar_width() + kBarPadding; |
| |
| if (panel != dragged_panel_) { |
| panel->Move(right, kAnimMs); |
| } |
| } |
| } |
| |
| void PanelBar::RepositionExpandedPanels(Panel* fixed_panel) { |
| CHECK(fixed_panel); |
| |
| // First, find the index of the fixed panel. |
| int fixed_index = GetPanelIndex(expanded_panels_, *fixed_panel); |
| CHECK_LT(fixed_index, static_cast<int>(expanded_panels_.size())); |
| |
| // Next, check if the panel has moved to the other side of another panel. |
| const int center_x = fixed_panel->cur_panel_center(); |
| for (size_t i = 0; i < expanded_panels_.size(); ++i) { |
| Panel* panel = expanded_panels_[i].get(); |
| if (center_x <= panel->cur_panel_center() || |
| i == expanded_panels_.size() - 1) { |
| if (panel != fixed_panel) { |
| // If it has, then we reorder the panels. |
| std::tr1::shared_ptr<Panel> ref = expanded_panels_[fixed_index]; |
| expanded_panels_.erase(expanded_panels_.begin() + fixed_index); |
| if (i < expanded_panels_.size()) { |
| expanded_panels_.insert(expanded_panels_.begin() + i, ref); |
| } else { |
| expanded_panels_.push_back(ref); |
| } |
| } |
| break; |
| } |
| } |
| |
| // Find the total width of the panels to the left of the fixed panel. |
| int total_width = 0; |
| fixed_index = -1; |
| for (int i = 0; i < static_cast<int>(expanded_panels_.size()); ++i) { |
| Panel* panel = expanded_panels_[i].get(); |
| if (panel == fixed_panel) { |
| fixed_index = i; |
| break; |
| } |
| total_width += panel->panel_width(); |
| } |
| CHECK_NE(fixed_index, -1); |
| int new_fixed_index = fixed_index; |
| |
| // Move panels over to the right of the fixed panel until all of the ones |
| // on the left will fit. |
| int avail_width = std::max(fixed_panel->cur_panel_left() - kBarPadding - x_, |
| 0); |
| while (total_width > avail_width) { |
| new_fixed_index--; |
| CHECK(new_fixed_index >= 0); |
| total_width -= expanded_panels_[new_fixed_index]->panel_width(); |
| } |
| |
| // Reorder the fixed panel if its index changed. |
| if (new_fixed_index != fixed_index) { |
| Panels::iterator it = expanded_panels_.begin() + fixed_index; |
| std::tr1::shared_ptr<Panel> ref = *it; |
| expanded_panels_.erase(it); |
| expanded_panels_.insert(expanded_panels_.begin() + new_fixed_index, ref); |
| fixed_index = new_fixed_index; |
| } |
| |
| // Now find the width of the panels to the right, and move them to the |
| // left as needed. |
| total_width = 0; |
| for (Panels::iterator it = expanded_panels_.begin() + fixed_index + 1; |
| it != expanded_panels_.end(); ++it) { |
| total_width += (*it)->panel_width(); |
| } |
| |
| avail_width = std::max(x_ + width_ - (fixed_panel->cur_right() + kBarPadding), |
| 0); |
| while (total_width > avail_width) { |
| new_fixed_index++; |
| CHECK_LT(new_fixed_index, static_cast<int>(expanded_panels_.size())); |
| total_width -= expanded_panels_[new_fixed_index]->panel_width(); |
| } |
| |
| // Do the reordering again. |
| if (new_fixed_index != fixed_index) { |
| Panels::iterator it = expanded_panels_.begin() + fixed_index; |
| std::tr1::shared_ptr<Panel> ref = *it; |
| expanded_panels_.erase(it); |
| expanded_panels_.insert(expanded_panels_.begin() + new_fixed_index, ref); |
| fixed_index = new_fixed_index; |
| } |
| |
| // Finally, push panels to the left and the right so they don't overlap. |
| int boundary = expanded_panels_[fixed_index]->cur_panel_left() - kBarPadding; |
| for (Panels::reverse_iterator it = |
| // Start at the panel to the left of 'new_fixed_index'. |
| expanded_panels_.rbegin() + |
| (expanded_panels_.size() - new_fixed_index); |
| it != expanded_panels_.rend(); ++it) { |
| Panel* panel = it->get(); |
| if (panel->cur_right() > boundary) { |
| panel->Move(boundary, kAnimMs); |
| } else if (panel->cur_panel_left() < x_) { |
| panel->Move(std::min(boundary, x_ + panel->panel_width() + kBarPadding), |
| kAnimMs); |
| } |
| boundary = panel->cur_panel_left() - kBarPadding; |
| } |
| |
| boundary = expanded_panels_[fixed_index]->cur_right() + kBarPadding; |
| for (Panels::iterator it = expanded_panels_.begin() + new_fixed_index + 1; |
| it != expanded_panels_.end(); ++it) { |
| Panel* panel = it->get(); |
| if (panel->cur_panel_left() < boundary) { |
| panel->Move(boundary + panel->panel_width(), kAnimMs); |
| } else if (panel->cur_right() > x_ + width_) { |
| panel->Move( |
| std::max(boundary + panel->panel_width(), width_ - kBarPadding), |
| kAnimMs); |
| } |
| boundary = panel->cur_right() + kBarPadding; |
| } |
| } |
| |
| void PanelBar::InsertCollapsedPanel(std::tr1::shared_ptr<Panel> new_panel) { |
| size_t index = 0; |
| for (; index < collapsed_panels_.size(); ++index) { |
| Panel* panel = collapsed_panels_[index].get(); |
| if (new_panel->cur_titlebar_left() < panel->cur_titlebar_left()) { |
| break; |
| } |
| } |
| collapsed_panels_.insert(collapsed_panels_.begin() + index, new_panel); |
| } |
| |
| void PanelBar::InsertExpandedPanel(std::tr1::shared_ptr<Panel> new_panel) { |
| size_t index = 0; |
| for (; index < expanded_panels_.size(); ++index) { |
| Panel* panel = expanded_panels_[index].get(); |
| if (new_panel->cur_panel_left() < panel->cur_panel_left()) { |
| break; |
| } |
| } |
| expanded_panels_.insert(expanded_panels_.begin() + index, new_panel); |
| } |
| |
| void PanelBar::CreateAnchor(Panel* panel) { |
| if (anchor_input_win_ != None) { |
| LOG(WARNING) << "Destroying extra input window " |
| << XidStr(anchor_input_win_); |
| wm_->xconn()->DestroyWindow(anchor_input_win_); |
| } |
| anchor_input_win_ = wm_->CreateInputWindow( |
| panel->cur_titlebar_left(), y_, panel->titlebar_width(), height_); |
| anchor_panel_ = panel; |
| |
| anchor_actor_->Move( |
| panel->cur_titlebar_left() + |
| 0.5 * (panel->titlebar_width() - anchor_actor_->GetWidth()), |
| y_ + 0.5 * (height_ - anchor_actor_->GetHeight()), |
| 0); // anim_ms |
| anchor_actor_->SetOpacity(1, kAnimMs); |
| } |
| |
| void PanelBar::DestroyAnchor() { |
| if (anchor_input_win_ != None) { |
| wm_->xconn()->DestroyWindow(anchor_input_win_); |
| anchor_input_win_ = None; |
| } |
| anchor_actor_->SetOpacity(0, kAnimMs); |
| anchor_panel_ = NULL; |
| PackCollapsedPanels(); |
| } |
| |
| Panel* PanelBar::GetNearestExpandedPanel(Panel* panel) { |
| if (!panel || !panel->is_expanded()) { |
| return NULL; |
| } |
| |
| Panel* nearest_panel = NULL; |
| int best_distance = kint32max; |
| for (Panels::iterator it = expanded_panels_.begin(); |
| it != expanded_panels_.end(); ++it) { |
| if (it->get() == panel) { |
| continue; |
| } |
| |
| int distance = kint32max; |
| if ((*it)->cur_right() <= panel->cur_panel_left()) { |
| distance = panel->cur_panel_left() - (*it)->cur_right(); |
| } else if ((*it)->cur_panel_left() >= panel->cur_right()) { |
| distance = (*it)->cur_panel_left() - panel->cur_right(); |
| } else { |
| distance = abs((*it)->cur_panel_center() - panel->cur_panel_center()); |
| } |
| |
| if (distance < best_distance) { |
| best_distance = distance; |
| nearest_panel = it->get(); |
| } |
| } |
| return nearest_panel; |
| } |
| |
| void PanelBar::SetVisibility(bool visible) { |
| if (is_visible_ == visible) { |
| return; |
| } |
| |
| if (visible) { |
| bar_actor_->MoveY(y_, kAnimMs); |
| bar_shadow_->MoveY(y_, kAnimMs); |
| bar_shadow_->SetOpacity(1, kAnimMs); |
| } else { |
| bar_actor_->MoveY(y_ + height_, kAnimMs); |
| bar_shadow_->MoveY(y_ + height_, kAnimMs); |
| bar_shadow_->SetOpacity(0, kAnimMs); |
| } |
| is_visible_ = visible; |
| wm_->HandlePanelBarVisibilityChange(visible); |
| } |
| |
| } // namespace chromeos |