summaryrefslogtreecommitdiff
diff options
-rw-r--r--dash/DashView.cpp13
-rwxr-xr-xdash/LensView.cpp16
-rw-r--r--dash/previews/ApplicationPreview.cpp4
-rw-r--r--dash/previews/GenericPreview.cpp4
-rw-r--r--dash/previews/MoviePreview.cpp4
-rw-r--r--dash/previews/SocialPreview.cpp4
-rw-r--r--dash/previews/Tracks.cpp4
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/test_overlay_scrollbar.cpp280
-rw-r--r--unity-shared/CMakeLists.txt2
-rw-r--r--unity-shared/PlacesOverlayVScrollBar.cpp417
-rw-r--r--unity-shared/PlacesOverlayVScrollBar.h107
-rw-r--r--unity-shared/VScrollBarOverlayWindow.cpp526
-rw-r--r--unity-shared/VScrollBarOverlayWindow.h92
14 files changed, 1459 insertions, 15 deletions
diff --git a/dash/DashView.cpp b/dash/DashView.cpp
index 566f86b59..ce9f86503 100644
--- a/dash/DashView.cpp
+++ b/dash/DashView.cpp
@@ -166,6 +166,7 @@ void DashView::ClosePreview()
preview_navigation_mode_ = previews::Navigation::NONE;
preview_displaying_ = false;
+ active_lens_view_->SetVisible(true);
// re-focus dash view component.
nux::GetWindowCompositor().SetKeyFocusArea(default_focus());
@@ -269,6 +270,7 @@ void DashView::BuildPreview(Preview::Ptr model)
preview_container_->SetGeometry(layout_->GetGeometry());
preview_displaying_ = true;
+ active_lens_view_->SetVisible(false);
// connect to nav left/right signals to request nav left/right movement.
preview_container_->navigate_left.connect([&] () {
@@ -328,18 +330,19 @@ void DashView::AboutToShow()
LOG_DEBUG(logger) << "Setting ViewType " << ViewType::LENS_VIEW
<< " on '" << home_lens_->id() << "'";
}
- else if (active_lens_view_)
+ else
{
// careful here, the lens_view's view_type doesn't get reset when the dash
// hides, but lens' view_type does, so we need to update the lens directly
active_lens_view_->lens()->view_type = ViewType::LENS_VIEW;
}
+ active_lens_view_->SetVisible(true);
// this will make sure the spinner animates if the search takes a while
search_bar_->ForceSearchChanged();
// if a preview is open, close it
- if (preview_displaying_)
+ if (preview_displaying_)
{
ClosePreview();
}
@@ -363,8 +366,10 @@ void DashView::AboutToHide()
LOG_DEBUG(logger) << "Setting ViewType " << ViewType::HIDDEN
<< " on '" << home_lens_->id() << "'";
+ active_lens_view_->SetVisible(false);
+
// if a preview is open, close it
- if (preview_displaying_)
+ if (preview_displaying_)
{
ClosePreview();
}
@@ -967,6 +972,8 @@ void DashView::OnLensBarActivated(std::string const& id)
return;
}
+ lens_views_[id]->SetVisible(true);
+ active_lens_view_->SetVisible(false);
LensView* view = active_lens_view_ = lens_views_[id];
view->JumpToTop();
diff --git a/dash/LensView.cpp b/dash/LensView.cpp
index 815aa49c3..0442ebba5 100755
--- a/dash/LensView.cpp
+++ b/dash/LensView.cpp
@@ -33,6 +33,7 @@
#include "unity-shared/UBusMessages.h"
#include "unity-shared/UBusWrapper.h"
#include "unity-shared/PlacesVScrollBar.h"
+#include "unity-shared/PlacesOverlayVScrollBar.h"
#include <glib/gi18n-lib.h>
@@ -57,6 +58,13 @@ public:
, up_area_(nullptr)
{
SetVScrollBar(scroll_bar);
+
+ OnVisibleChanged.connect([&] (nux::Area* /*area*/, bool visible) {
+ if (m_horizontal_scrollbar_enable)
+ _hscrollbar->SetVisible(visible);
+ if (m_vertical_scrollbar_enable)
+ _vscrollbar->SetVisible(visible);
+ });
}
void ScrollToPosition(nux::Geometry const& position)
@@ -192,6 +200,10 @@ LensView::LensView(Lens::Ptr lens, nux::Area* show_filters)
}
});
+ OnVisibleChanged.connect([&] (nux::Area* area, bool visible) {
+ scroll_view_->SetVisible(visible);
+ });
+
}
void LensView::SetupViews(nux::Area* show_filters)
@@ -201,9 +213,9 @@ void LensView::SetupViews(nux::Area* show_filters)
layout_ = new nux::HLayout(NUX_TRACKER_LOCATION);
layout_->SetSpaceBetweenChildren(style.GetSpaceBetweenLensAndFilters());
- scroll_view_ = new LensScrollView(new PlacesVScrollBar(NUX_TRACKER_LOCATION),
+ scroll_view_ = new LensScrollView(new PlacesOverlayVScrollBar(NUX_TRACKER_LOCATION),
NUX_TRACKER_LOCATION);
- scroll_view_->EnableVerticalScrollBar(false);
+ scroll_view_->EnableVerticalScrollBar(true);
scroll_view_->EnableHorizontalScrollBar(false);
layout_->AddView(scroll_view_);
diff --git a/dash/previews/ApplicationPreview.cpp b/dash/previews/ApplicationPreview.cpp
index ebc994807..76c377c7f 100644
--- a/dash/previews/ApplicationPreview.cpp
+++ b/dash/previews/ApplicationPreview.cpp
@@ -25,7 +25,7 @@
#include "unity-shared/CoverArt.h"
#include "unity-shared/IconTexture.h"
#include "unity-shared/StaticCairoText.h"
-#include "unity-shared/PlacesVScrollBar.h"
+#include "unity-shared/PlacesOverlayVScrollBar.h"
#include <UnityCore/ApplicationPreview.h>
#include <NuxCore/Logger.h>
#include <Nux/HLayout.h>
@@ -54,7 +54,7 @@ public:
DetailsScrollView(NUX_FILE_LINE_PROTO)
: ScrollView(NUX_FILE_LINE_PARAM)
{
- SetVScrollBar(new dash::PlacesVScrollBar(NUX_TRACKER_LOCATION));
+ SetVScrollBar(new dash::PlacesOverlayVScrollBar(NUX_TRACKER_LOCATION));
}
};
diff --git a/dash/previews/GenericPreview.cpp b/dash/previews/GenericPreview.cpp
index 3b396299c..b6ef3b09b 100644
--- a/dash/previews/GenericPreview.cpp
+++ b/dash/previews/GenericPreview.cpp
@@ -24,7 +24,7 @@
#include "unity-shared/PreviewStyle.h"
#include "unity-shared/CoverArt.h"
#include "unity-shared/StaticCairoText.h"
-#include "unity-shared/PlacesVScrollBar.h"
+#include "unity-shared/PlacesOverlayVScrollBar.h"
#include <NuxCore/Logger.h>
#include <Nux/HLayout.h>
#include <Nux/VLayout.h>
@@ -49,7 +49,7 @@ public:
DetailsScrollView(NUX_FILE_LINE_PROTO)
: ScrollView(NUX_FILE_LINE_PARAM)
{
- SetVScrollBar(new dash::PlacesVScrollBar(NUX_TRACKER_LOCATION));
+ SetVScrollBar(new dash::PlacesOverlayVScrollBar(NUX_TRACKER_LOCATION));
}
};
diff --git a/dash/previews/MoviePreview.cpp b/dash/previews/MoviePreview.cpp
index 653bd7a8d..5d5e29941 100644
--- a/dash/previews/MoviePreview.cpp
+++ b/dash/previews/MoviePreview.cpp
@@ -24,7 +24,7 @@
#include "unity-shared/PreviewStyle.h"
#include "unity-shared/CoverArt.h"
#include "unity-shared/StaticCairoText.h"
-#include "unity-shared/PlacesVScrollBar.h"
+#include "unity-shared/PlacesOverlayVScrollBar.h"
#include <UnityCore/MoviePreview.h>
#include <NuxCore/Logger.h>
#include <Nux/HLayout.h>
@@ -50,7 +50,7 @@ public:
DetailsScrollView(NUX_FILE_LINE_PROTO)
: ScrollView(NUX_FILE_LINE_PARAM)
{
- SetVScrollBar(new dash::PlacesVScrollBar(NUX_TRACKER_LOCATION));
+ SetVScrollBar(new dash::PlacesOverlayVScrollBar(NUX_TRACKER_LOCATION));
}
};
diff --git a/dash/previews/SocialPreview.cpp b/dash/previews/SocialPreview.cpp
index 1a4136381..51079d81b 100644
--- a/dash/previews/SocialPreview.cpp
+++ b/dash/previews/SocialPreview.cpp
@@ -25,7 +25,7 @@
#include "unity-shared/CoverArt.h"
#include "unity-shared/IconTexture.h"
#include "unity-shared/StaticCairoText.h"
-#include "unity-shared/PlacesVScrollBar.h"
+#include "unity-shared/PlacesOverlayVScrollBar.h"
#include <UnityCore/SocialPreview.h>
#include <NuxCore/Logger.h>
#include <Nux/HLayout.h>
@@ -54,7 +54,7 @@ public:
DetailsScrollView(NUX_FILE_LINE_PROTO)
: ScrollView(NUX_FILE_LINE_PARAM)
{
- SetVScrollBar(new dash::PlacesVScrollBar(NUX_TRACKER_LOCATION));
+ SetVScrollBar(new dash::PlacesOverlayVScrollBar(NUX_TRACKER_LOCATION));
}
};
diff --git a/dash/previews/Tracks.cpp b/dash/previews/Tracks.cpp
index 523270ac7..1c5a3872c 100644
--- a/dash/previews/Tracks.cpp
+++ b/dash/previews/Tracks.cpp
@@ -24,7 +24,7 @@
#include <NuxCore/Logger.h>
#include <Nux/VLayout.h>
#include "unity-shared/IntrospectableWrappers.h"
-#include "unity-shared/PlacesVScrollBar.h"
+#include "unity-shared/PlacesOverlayVScrollBar.h"
#include "unity-shared/PreviewStyle.h"
#include <UnityCore/Track.h>
#include <UnityCore/Variant.h>
@@ -76,7 +76,7 @@ void Tracks::AddProperties(GVariantBuilder* builder)
void Tracks::SetupViews()
{
- SetVScrollBar(new dash::PlacesVScrollBar(NUX_TRACKER_LOCATION));
+ SetVScrollBar(new dash::PlacesOverlayVScrollBar(NUX_TRACKER_LOCATION));
EnableHorizontalScrollBar(false);
layout_ = new nux::VLayout();
layout_->SetPadding(0, previews::Style::Instance().GetDetailsRightMargin(), 0, 0);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 71f55b6fc..5b36e4a3d 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -233,6 +233,7 @@ if (ENABLE_X_SUPPORT)
test_previews_movie.cpp
test_previews_music.cpp
test_previews_social.cpp
+ test_overlay_scrollbar.cpp
test_quicklist_menu_item.cpp
test_quicklist_view.cpp
test_resultviewgrid.cpp
diff --git a/tests/test_overlay_scrollbar.cpp b/tests/test_overlay_scrollbar.cpp
new file mode 100644
index 000000000..d1c3cc13d
--- /dev/null
+++ b/tests/test_overlay_scrollbar.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 3, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the applicable version of the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of both the GNU Lesser General Public
+ * License version 3 along with this program. If not, see
+ * <http://www.gnu.org/licenses/>
+ *
+ * Authored by: Brandon Schaefer <brandon.schaefer@canonical.com>
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <Nux/Nux.h>
+#include <NuxCore/ObjectPtr.h>
+#include <Nux/VLayout.h>
+
+#include "unity-shared/VScrollBarOverlayWindow.h"
+#include "unity-shared/PlacesOverlayVScrollBar.h"
+#include "unity-shared/UScreen.h"
+
+using namespace testing;
+
+namespace
+{
+
+class TestOverlayWindow : public Test
+{
+public:
+ TestOverlayWindow()
+ {
+ overlay_window_ = new VScrollBarOverlayWindow(nux::Geometry(0,0,100,100));
+ }
+
+ int GetProxListSize() const
+ {
+ return nux::GetWindowThread()->GetWindowCompositor().GetProximityListSize();
+ }
+
+ nux::ObjectPtr<VScrollBarOverlayWindow> overlay_window_;
+};
+
+class TestOverlayScrollBar : public Test
+{
+public:
+ class MockScrollBar : public unity::dash::PlacesOverlayVScrollBar
+ {
+ public:
+ MockScrollBar(NUX_FILE_LINE_DECL)
+ : unity::dash::PlacesOverlayVScrollBar(NUX_FILE_LINE_PARAM)
+ , scroll_dy(0)
+ , scroll_up_signal_(false)
+ , scroll_down_signal_(false)
+ {
+ SetGeometry(nux::Geometry(0,0,200,500));
+ SetContainerSize(0,0,200,200);
+ SetContentSize(0,0,200,2000);
+ ComputeContentSize();
+
+ OnScrollUp.connect([&] (float step, int dy) {
+ scroll_dy = dy;
+ scroll_up_signal_ = true;
+ });
+
+ OnScrollDown.connect([&] (float step, int dy) {
+ scroll_dy = dy;
+ scroll_down_signal_ = true;
+ });
+ }
+
+ void ScrollDown(int scroll_dy)
+ {
+ // Shows we are over the Overlay Thumb
+ int x = _track->GetBaseX() + _track->GetBaseWidth() + 5;
+ int y = _track->GetBaseY();
+
+ MoveMouse(x,y);
+ MoveDown(x,y);
+
+ MoveMouse(x,y+scroll_dy);
+ MoveUp(x,y+scroll_dy);
+ }
+
+ void ScrollUp(int scroll_dy)
+ {
+ ScrollDown(scroll_dy);
+
+ // Shows we are over the Overlay Thumb
+ int x = _track->GetBaseX() + _track->GetBaseWidth() + 5;
+ int y = _track->GetBaseY();
+
+ MoveMouse(x,y+scroll_dy);
+ MoveDown(x,y+scroll_dy);
+
+ MoveMouse(x,y);
+ MoveUp(x,y);
+ }
+
+ void MoveDown(int x, int y)
+ {
+ nux::Event event;
+ event.type = nux::NUX_MOUSE_PRESSED;
+ event.x = x;
+ event.y = y;
+ nux::GetWindowCompositor().ProcessEvent(event);
+ }
+
+ void MoveUp(int x, int y)
+ {
+ nux::Event event;
+ event.type = nux::NUX_MOUSE_RELEASED;
+ event.x = x;
+ event.y = y;
+ nux::GetWindowCompositor().ProcessEvent(event);
+ }
+
+ void MoveMouse(int x, int y)
+ {
+ nux::Event event;
+ event.type = nux::NUX_MOUSE_MOVE;
+ event.x = x;
+ event.y = y;
+ nux::GetWindowCompositor().ProcessEvent(event);
+ }
+
+ using nux::VScrollBar::AtMinimum;
+ using nux::VScrollBar::GetBaseHeight;
+
+ int scroll_dy;
+ bool scroll_up_signal_;
+ bool scroll_down_signal_;
+ };
+
+ TestOverlayScrollBar()
+ {
+ scroll_bar_ = std::make_shared<MockScrollBar>(NUX_TRACKER_LOCATION);
+ }
+
+ std::shared_ptr<MockScrollBar> scroll_bar_;
+};
+
+TEST_F(TestOverlayWindow, TestOverlayShows)
+{
+ overlay_window_->MouseNear();
+ EXPECT_TRUE(overlay_window_->IsVisible());
+}
+
+TEST_F(TestOverlayWindow, TestOverlayHides)
+{
+ overlay_window_->MouseNear();
+ EXPECT_TRUE(overlay_window_->IsVisible());
+
+ overlay_window_->MouseBeyond();
+ EXPECT_FALSE(overlay_window_->IsVisible());
+}
+
+TEST_F(TestOverlayWindow, TestOverlayStaysOpenWhenMouseDown)
+{
+ overlay_window_->MouseNear();
+ overlay_window_->MouseDown();
+
+ overlay_window_->MouseBeyond();
+ EXPECT_TRUE(overlay_window_->IsVisible());
+}
+
+TEST_F(TestOverlayWindow, TestOverlayMouseDrags)
+{
+ overlay_window_->MouseDown();
+ EXPECT_FALSE(overlay_window_->IsMouseBeingDragged());
+
+ overlay_window_->SetThumbOffsetY(10);
+ EXPECT_TRUE(overlay_window_->IsMouseBeingDragged());
+}
+
+TEST_F(TestOverlayWindow, TestOverlayStopDraggingOnMouseUp)
+{
+ overlay_window_->MouseDown();
+ EXPECT_FALSE(overlay_window_->IsMouseBeingDragged());
+
+ overlay_window_->SetThumbOffsetY(10);
+ EXPECT_TRUE(overlay_window_->IsMouseBeingDragged());
+
+ overlay_window_->MouseUp();
+ EXPECT_FALSE(overlay_window_->IsMouseBeingDragged());
+}
+
+TEST_F(TestOverlayWindow, TestOverlaySetsOffsetY)
+{
+ int const offset_y = 30;
+
+ overlay_window_->SetThumbOffsetY(offset_y);
+ EXPECT_EQ(overlay_window_->GetThumbOffsetY(), offset_y);
+}
+
+TEST_F(TestOverlayWindow, TestOverlaySetsOffsetYOutOfBoundsLower)
+{
+ int const offset_y = -40;
+
+ overlay_window_->SetThumbOffsetY(offset_y);
+ EXPECT_EQ(overlay_window_->GetThumbOffsetY(), 0);
+}
+
+TEST_F(TestOverlayWindow, TestOverlaySetsOffsetYOutOfBoundsUpper)
+{
+ int const offset_y = 1000;
+ int const expected_offset = overlay_window_->GetBaseHeight() - overlay_window_->GetThumbHeight();
+
+ overlay_window_->SetThumbOffsetY(offset_y);
+ EXPECT_EQ(overlay_window_->GetThumbOffsetY(), expected_offset);
+}
+
+TEST_F(TestOverlayWindow, TestOverlayMouseIsInsideThumb)
+{
+ nux::Geometry const geo(0, 50, 50, 400);
+
+ overlay_window_->UpdateGeometry(geo);
+ EXPECT_TRUE(overlay_window_->IsMouseInsideThumb(0));
+}
+
+TEST_F(TestOverlayWindow, TestOverlayMouseIsInsideOnOffsetChange)
+{
+ nux::Geometry const geo(0, 50, 50, 400);
+ int const offset_y = 50;
+ int const thumb_height = overlay_window_->GetThumbHeight();
+
+ overlay_window_->UpdateGeometry(geo);
+ overlay_window_->SetThumbOffsetY(offset_y);
+
+ EXPECT_FALSE(overlay_window_->IsMouseInsideThumb(offset_y - 1));
+ EXPECT_TRUE(overlay_window_->IsMouseInsideThumb(offset_y + thumb_height/2));
+ EXPECT_FALSE(overlay_window_->IsMouseInsideThumb(offset_y + thumb_height + 1));
+}
+
+TEST_F(TestOverlayScrollBar, TestScrollDownSignal)
+{
+ scroll_bar_->ScrollDown(10);
+ EXPECT_TRUE(scroll_bar_->scroll_down_signal_);
+}
+
+TEST_F(TestOverlayScrollBar, TestScrollUpSignal)
+{
+ scroll_bar_->ScrollUp(10);
+ EXPECT_TRUE(scroll_bar_->scroll_up_signal_);
+}
+
+TEST_F(TestOverlayScrollBar, TestScrollDownDeltaY)
+{
+ int scroll_down = 15;
+ scroll_bar_->ScrollDown(scroll_down);
+ EXPECT_EQ(scroll_bar_->scroll_dy, scroll_down);
+}
+
+TEST_F(TestOverlayScrollBar, TestScrollUpDeltaY)
+{
+ int scroll_up = 7;
+ scroll_bar_->ScrollUp(scroll_up);
+ EXPECT_EQ(scroll_bar_->scroll_dy, scroll_up);
+}
+
+TEST_F(TestOverlayScrollBar, TestScrollsSlowlyDeltaY)
+{
+ int scroll_down = 10;
+ for (int i = 0; i < scroll_down; i++)
+ {
+ scroll_bar_->ScrollDown(1);
+ EXPECT_EQ(scroll_bar_->scroll_dy, 1);
+ }
+}
+
+}
diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt
index e8246f0ec..355e5bfe0 100644
--- a/unity-shared/CMakeLists.txt
+++ b/unity-shared/CMakeLists.txt
@@ -49,6 +49,7 @@ set (UNITY_SHARED_SOURCES
OverlayRenderer.cpp
PanelStyle.cpp
PlacesVScrollBar.cpp
+ PlacesOverlayVScrollBar.cpp
PreviewStyle.cpp
RatingsButton.cpp
SearchBar.cpp
@@ -66,6 +67,7 @@ set (UNITY_SHARED_SOURCES
UnityWindowStyle.cpp
UnityWindowView.cpp
UserThumbnailProvider.cpp
+ VScrollBarOverlayWindow.cpp
WindowManager.cpp
XPathQueryPart.cpp
)
diff --git a/unity-shared/PlacesOverlayVScrollBar.cpp b/unity-shared/PlacesOverlayVScrollBar.cpp
new file mode 100644
index 000000000..e318cdf3c
--- /dev/null
+++ b/unity-shared/PlacesOverlayVScrollBar.cpp
@@ -0,0 +1,417 @@
+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
+/*
+ * Copyright (C) 2012 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Brandon Schaefer <brandon.schaefer@canonical.com>
+ */
+
+#include <Nux/Nux.h>
+
+#include "PlacesOverlayVScrollBar.h"
+#include "CairoTexture.h"
+
+namespace
+{
+ int const PROXIMITY = 7;
+ int const SCROLL_ANIMATION = 400;
+ int const MAX_CONNECTOR_ANIMATION = 200;
+}
+
+namespace unity
+{
+namespace dash
+{
+
+PlacesOverlayVScrollBar::PlacesOverlayVScrollBar(NUX_FILE_LINE_DECL)
+ : PlacesVScrollBar(NUX_FILE_LINE_PARAM)
+ , overlay_window_(new VScrollBarOverlayWindow(_track->GetAbsoluteGeometry()))
+ , area_prox_(overlay_window_.GetPointer(), PROXIMITY)
+ , thumb_above_slider_(false)
+ , connector_height_(0)
+ , mouse_down_offset_(0)
+ , delta_update_(0)
+{
+ area_prox_.mouse_near.connect(sigc::mem_fun(this, &PlacesOverlayVScrollBar::OnMouseNear));
+ area_prox_.mouse_beyond.connect(sigc::mem_fun(this, &PlacesOverlayVScrollBar::OnMouseBeyond));
+
+ overlay_window_->mouse_down.connect(sigc::mem_fun(this, &PlacesOverlayVScrollBar::OnMouseDown));
+ overlay_window_->mouse_up.connect(sigc::mem_fun(this, &PlacesOverlayVScrollBar::OnMouseUp));
+ overlay_window_->mouse_click.connect(sigc::mem_fun(this, &PlacesOverlayVScrollBar::OnMouseClick));
+ overlay_window_->mouse_move.connect(sigc::mem_fun(this, &PlacesOverlayVScrollBar::OnMouseMove));
+ overlay_window_->mouse_drag.connect(sigc::mem_fun(this, &PlacesOverlayVScrollBar::OnMouseDrag));
+
+ _track->geometry_changed.connect(sigc::mem_fun(this, &PlacesOverlayVScrollBar::OnTrackGeometryChanged));
+ OnVisibleChanged.connect(sigc::mem_fun(this, &PlacesOverlayVScrollBar::OnVisibilityChanged));
+}
+
+void PlacesOverlayVScrollBar::OnTrackGeometryChanged(nux::Area* /*area*/, nux::Geometry& /*geo*/)
+{
+ UpdateStepY();
+ overlay_window_->UpdateGeometry(_track->GetAbsoluteGeometry());
+
+ if (overlay_window_->IsVisible() && !IsScrollBarVisible())
+ {
+ overlay_window_->ResetStates();
+ ResetConnector();
+ }
+}
+
+void PlacesOverlayVScrollBar::OnVisibilityChanged(nux::Area* /*area*/, bool visible)
+{
+ if (overlay_window_->IsVisible() && !visible)
+ {
+ overlay_window_->ResetStates();
+ ResetConnector();
+ }
+}
+
+void PlacesOverlayVScrollBar::StopAnimation()
+{
+ if (animation_.CurrentState() != nux::animation::Animation::State::Stopped)
+ animation_.Stop();
+}
+
+void PlacesOverlayVScrollBar::SetupAnimation(int start, int stop, int milliseconds)
+{
+ tweening_connection_.disconnect();
+ delta_update_ = 0;
+
+ animation_.SetDuration(milliseconds);
+ animation_.SetEasingCurve(nux::animation::EasingCurve(nux::animation::EasingCurve::Type::Linear));
+
+ animation_.SetStartValue(start);
+ animation_.SetFinishValue(stop);
+}
+
+void PlacesOverlayVScrollBar::StartScrollAnimation(ScrollDir dir, int stop)
+{
+ if (animation_.CurrentState() == nux::animation::Animation::State::Stopped)
+ {
+ SetupAnimation(0, stop, SCROLL_ANIMATION);
+
+ tweening_connection_ = animation_.updated.connect([this, dir] (int const& update) {
+ OnScroll(dir, update - delta_update_);
+ delta_update_ = update;
+
+ CheckIfThumbIsInsideSlider();
+ UpdateConnectorPosition();
+ QueueDraw();
+ });
+
+ animation_.Start();
+ }
+}
+
+void PlacesOverlayVScrollBar::OnScroll(ScrollDir dir, int mouse_dy)
+{
+ if (dir == ScrollDir::UP)
+ OnScrollUp.emit(stepY, mouse_dy);
+ else if (dir == ScrollDir::DOWN)
+ OnScrollDown.emit(stepY, mouse_dy);
+}
+
+void PlacesOverlayVScrollBar::StartConnectorAnimation()
+{
+ if (animation_.CurrentState() == nux::animation::Animation::State::Stopped)
+ {
+ SetupAnimation(connector_height_, 0, std::min(connector_height_, MAX_CONNECTOR_ANIMATION));
+
+ tweening_connection_ = animation_.updated.connect([this] (int const& update) {
+ connector_height_ = update;
+ UpdateConnectorTexture();
+ });
+
+ animation_.Start();
+ }
+}
+
+bool PlacesOverlayVScrollBar::IsScrollBarVisible() const
+{
+ return (content_height_ > container_height_);
+}
+
+void PlacesOverlayVScrollBar::OnMouseNear(nux::Point const& mouse_pos)
+{
+ if (IsVisible() && IsScrollBarVisible())
+ {
+ StopAnimation();
+
+ overlay_window_->MouseNear();
+ AdjustThumbOffsetFromMouse();
+ }
+}
+
+void PlacesOverlayVScrollBar::OnMouseBeyond(nux::Point const& mouse_pos)
+{
+ if (IsVisible() && IsScrollBarVisible())
+ {
+ overlay_window_->MouseBeyond();
+ UpdateConnectorPosition();
+ }
+}
+
+void PlacesOverlayVScrollBar::AdjustThumbOffsetFromMouse()
+{
+ if (!overlay_window_->IsMouseBeingDragged())
+ {
+ nux::Point const& mouse = nux::GetWindowCompositor().GetMousePosition();
+
+ if (mouse.y > 0)
+ {
+ int const quarter_of_thumb = overlay_window_->GetThumbHeight()/4;
+ int const new_offset = mouse.y - _track->GetAbsoluteY() - overlay_window_->GetThumbHeight()/2;
+
+ int const slider_offset = _slider->GetAbsoluteY() - _track->GetAbsoluteY() + _slider->GetBaseHeight()/3;
+ bool const mouse_above_slider = slider_offset < new_offset;
+
+ if (mouse_above_slider)
+ overlay_window_->SetThumbOffsetY(new_offset - quarter_of_thumb);
+ else
+ overlay_window_->SetThumbOffsetY(new_offset + quarter_of_thumb);
+ }
+
+ CheckIfThumbIsInsideSlider();
+ }
+}
+
+void PlacesOverlayVScrollBar::CheckIfThumbIsInsideSlider()
+{
+ nux::Geometry const& slider_geo = _slider->GetAbsoluteGeometry();
+ nux::Geometry const& thumb_geo = overlay_window_->GetThumbGeometry();
+ nux::Geometry const& intersection = (thumb_geo.Intersect(slider_geo));
+
+ if (!intersection.IsNull())
+ {
+ ResetConnector();
+ overlay_window_->ThumbInsideSlider();
+ }
+ else
+ {
+ UpdateConnectorPosition();
+ overlay_window_->ThumbOutsideSlider();
+ }
+}
+
+void PlacesOverlayVScrollBar::UpdateConnectorPosition()
+{
+ int const slider_y = _slider->GetBaseY() - GetBaseY();
+ int const thumb_y = overlay_window_->GetThumbOffsetY();
+ int const thumb_height = overlay_window_->GetThumbHeight();
+
+ if (!overlay_window_->IsVisible())
+ {
+ ResetConnector();
+ }
+ else if (slider_y > thumb_y)
+ {
+ thumb_above_slider_ = true;
+ connector_height_ = slider_y - (thumb_y + thumb_height);
+ }
+ else
+ {
+ thumb_above_slider_ = false;
+ connector_height_ = thumb_y - (_slider->GetBaseY() + _slider->GetBaseHeight()) + _track->GetBaseY();
+ }
+
+ UpdateConnectorTexture();
+}
+
+void PlacesOverlayVScrollBar::ResetConnector()
+{
+ StartConnectorAnimation();
+ QueueDraw();
+}
+
+void PlacesOverlayVScrollBar::OnMouseClick(int /*x*/, int y, unsigned int button_flags, unsigned int /*key_flags*/)
+{
+ if (!overlay_window_->IsMouseBeingDragged())
+ {
+ int const button = nux::GetEventButton(button_flags);
+
+ if (button == 1)
+ LeftMouseClick(y);
+ else if (button == 2)
+ MiddleMouseClick(y);
+ }
+
+ overlay_window_->MouseUp();
+}
+
+void PlacesOverlayVScrollBar::LeftMouseClick(int y)
+{
+ if (IsMouseInTopHalfOfThumb(y))
+ {
+ int const top = _slider->GetBaseY() - _track->GetBaseY();
+ StartScrollAnimation(ScrollDir::UP, std::min(_slider->GetBaseHeight(), top));
+ }
+ else
+ {
+ int const bottom = (_track->GetBaseY() + _track->GetBaseHeight()) -
+ (_slider->GetBaseHeight() + _slider->GetBaseY());
+ StartScrollAnimation(ScrollDir::DOWN, std::min(_slider->GetBaseHeight(), bottom));
+ }
+
+ UpdateConnectorPosition();
+}
+
+void PlacesOverlayVScrollBar::MiddleMouseClick(int y)
+{
+ int const slider_offset = _slider->GetBaseY() - _track->GetBaseY();
+ bool const move_up = slider_offset > overlay_window_->GetThumbOffsetY();
+
+ int const slider_thumb_diff = abs(overlay_window_->GetThumbOffsetY() - slider_offset);
+
+ if (move_up)
+ StartScrollAnimation(ScrollDir::UP, slider_thumb_diff);
+ else
+ StartScrollAnimation(ScrollDir::DOWN, slider_thumb_diff);
+}
+
+void PlacesOverlayVScrollBar::OnMouseDown(int /*x*/, int y, unsigned int /*button_flags*/, unsigned int /*key_flags*/)
+{
+ if (overlay_window_->IsMouseInsideThumb(y))
+ {
+ if (IsMouseInTopHalfOfThumb(y))
+ overlay_window_->PageUpAction();
+ else
+ overlay_window_->PageDownAction();
+
+ mouse_down_offset_ = y - overlay_window_->GetThumbOffsetY();
+ overlay_window_->MouseDown();
+ }
+}
+
+bool PlacesOverlayVScrollBar::IsMouseInTopHalfOfThumb(int y)
+{
+ int const thumb_height = overlay_window_->GetThumbHeight();
+ int const thumb_offset_y = overlay_window_->GetThumbOffsetY();
+
+ return (y < (thumb_height/2 + thumb_offset_y));
+}
+
+void PlacesOverlayVScrollBar::OnMouseUp(int x, int y, unsigned int /*button_flags*/, unsigned int /*key_flags*/)
+{
+ nux::Geometry const& geo = overlay_window_->GetAbsoluteGeometry();
+
+ if (!geo.IsPointInside(x + geo.x, y + geo.y))
+ {
+ overlay_window_->MouseUp();
+ UpdateConnectorPosition();
+ }
+}
+
+void PlacesOverlayVScrollBar::OnMouseMove(int /*x*/, int y, int /*dx*/, int /*dy*/, unsigned int /*button_flags*/, unsigned int /*key_flags*/)
+{
+ if (!overlay_window_->IsMouseInsideThumb(y))
+ AdjustThumbOffsetFromMouse();
+}
+
+void PlacesOverlayVScrollBar::OnMouseDrag(int /*x*/, int y, int /*dx*/, int dy, unsigned int /*button_flags*/, unsigned int /*key_flags*/)
+{
+ StopAnimation();
+ MouseDraggingOverlay(y, dy);
+}
+
+void PlacesOverlayVScrollBar::MouseDraggingOverlay(int y, int dys)
+{
+ int const dy = y - overlay_window_->GetThumbOffsetY() - mouse_down_offset_;
+ int const at_min = overlay_window_->GetThumbOffsetY() <= 0;
+ int const at_max = overlay_window_->GetThumbOffsetY() + overlay_window_->GetThumbHeight() >= _track->GetBaseHeight();
+
+ if (dy < 0 && !at_min)
+ {
+ OnScrollUp.emit(stepY, abs(dy));
+ }
+ else if (dy > 0 && !at_max)
+ {
+ OnScrollDown.emit(stepY, dy);
+ }
+
+ overlay_window_->SetThumbOffsetY(y - mouse_down_offset_);
+ CheckIfThumbIsInsideSlider();
+}
+
+void PlacesOverlayVScrollBar::UpdateStepY()
+{
+ stepY = (float) (content_height_ - container_height_) / (float) (_track->GetBaseHeight() - _slider->GetBaseHeight());
+}
+
+void PlacesOverlayVScrollBar::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw)
+{
+ PlacesVScrollBar::Draw(graphics_engine, force_draw);
+
+ if (connector_height_ > 0 && connector_texture_.IsValid())
+ {
+ int const connector_width = GetBaseWidth();
+ int offset_y = 0;
+ if (thumb_above_slider_)
+ {
+ offset_y = _slider->GetBaseY() - connector_height_;
+ }
+ else
+ {
+ offset_y = _slider->GetBaseY() + _slider->GetBaseHeight();
+ }
+
+ nux::Geometry base(_track->GetBaseX(), offset_y - 4, connector_width, connector_height_ + 5);
+ nux::TexCoordXForm texxform;
+
+ graphics_engine.QRP_1Tex(base.x,
+ base.y,
+ base.width,
+ base.height,
+ connector_texture_->GetDeviceTexture(),
+ texxform,
+ nux::color::White);
+ }
+}
+
+void PlacesOverlayVScrollBar::UpdateConnectorTexture()
+{
+ if (connector_height_ < 0)
+ return;
+
+ int width = 3;
+ int height = connector_height_;
+ float const radius = 1.5f;
+ float const aspect = 1.0f;
+
+ cairo_t* cr = NULL;
+
+ nux::color::RedGreenBlue const& connector_bg = nux::color::Gray;
+
+ nux::CairoGraphics cairoGraphics(CAIRO_FORMAT_ARGB32, width, height);
+ cr = cairoGraphics.GetContext();
+ cairo_save(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_save(cr);
+
+ cairo_set_source_rgba(cr, connector_bg.red, connector_bg.green, connector_bg.blue, 0.8);
+ cairoGraphics.DrawRoundedRectangle(cr, aspect, 0.0f, 0.0f, radius, width, height);
+ cairo_fill_preserve(cr);
+
+ connector_texture_.Adopt(texture_from_cairo_graphics(cairoGraphics));
+ cairo_destroy(cr);
+
+ QueueDraw();
+}
+
+} // namespace dash
+} // namespace unity
+
diff --git a/unity-shared/PlacesOverlayVScrollBar.h b/unity-shared/PlacesOverlayVScrollBar.h
new file mode 100644
index 000000000..f1b09403d
--- /dev/null
+++ b/unity-shared/PlacesOverlayVScrollBar.h
@@ -0,0 +1,107 @@
+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
+/*
+ * Copyright (C) 2012 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Brandon Schaefer <brandon.schaefer@canonical.com>
+ */
+
+#ifndef PLACES_OVERLAY_VSCROLLBAR_H
+#define PLACES_OVERLAY_VSCROLLBAR_H
+
+#include <Nux/Nux.h>
+#include <Nux/InputAreaProximity.h>
+#include <NuxCore/Animation.h>
+#include <memory>
+
+#include "unity-shared/PlacesVScrollBar.h"
+#include "unity-shared/VScrollBarOverlayWindow.h"
+
+namespace unity
+{
+namespace dash
+{
+
+class PlacesOverlayVScrollBar: public PlacesVScrollBar
+{
+public:
+ PlacesOverlayVScrollBar(NUX_FILE_LINE_PROTO);
+
+protected:
+ void Draw(nux::GraphicsEngine& graphics_engine, bool force_draw);
+
+private:
+ enum class ScrollDir : unsigned int
+ {
+ UP,
+ DOWN,
+ };
+
+ void OnTrackGeometryChanged(nux::Area* area, nux::Geometry& geo);
+ void OnVisibilityChanged(nux::Area* area, bool visible);
+
+ void OnMouseNear(nux::Point const& mouse_pos);
+ void OnMouseBeyond(nux::Point const& mouse_pos);
+ void AdjustThumbOffsetFromMouse();
+
+ void OnMouseClick(int x, int y, unsigned int button_flags, unsigned int key_flags);
+ void LeftMouseClick(int y);
+ void MiddleMouseClick(int y);
+
+ void OnMouseDown(int x, int y, unsigned int button_flags, unsigned int key_flags);
+ void OnMouseUp(int x, int y, unsigned int button_flags, unsigned int key_flags);
+
+ void OnMouseMove(int x, int y, int dx, int dy, unsigned int button_flags, unsigned int key_flags);
+
+ void OnMouseDrag(int x, int y, int dx, int dy, unsigned int button_flags, unsigned int key_flags);
+ void MouseDraggingOverlay(int y, int dy);
+
+ bool IsMouseInTopHalfOfThumb(int y);
+ void CheckIfThumbIsInsideSlider();
+
+ bool IsScrollBarVisible() const;
+
+ void UpdateConnectorPosition();
+ void ResetConnector();
+
+ void UpdateStepY();
+
+ void SetupAnimation(int start, int stop, int milliseconds);
+ void StopAnimation();
+
+ void StartScrollAnimation(ScrollDir dir, int stop);
+ void OnScroll(ScrollDir dir, int mouse_dy);
+
+ void StartConnectorAnimation();
+
+ void UpdateConnectorTexture();
+
+ nux::ObjectPtr<VScrollBarOverlayWindow> overlay_window_;
+ nux::InputAreaProximity area_prox_;
+
+ nux::animation::AnimateValue<int> animation_;
+ sigc::connection tweening_connection_;
+
+ nux::ObjectPtr<nux::BaseTexture> connector_texture_;
+
+ bool thumb_above_slider_;
+ int connector_height_;
+ int mouse_down_offset_;
+ int delta_update_;
+};
+
+} // namespace dash
+} // namespace unity
+
+#endif // PLACES_OVERLAY_VSCROLLBAR_H
diff --git a/unity-shared/VScrollBarOverlayWindow.cpp b/unity-shared/VScrollBarOverlayWindow.cpp
new file mode 100644
index 000000000..750feda63
--- /dev/null
+++ b/unity-shared/VScrollBarOverlayWindow.cpp
@@ -0,0 +1,526 @@
+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
+/*
+ * Copyright (C) 2012 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Brandon Schaefer <brandon.schaefer@canonical.com>
+ */
+
+#include <Nux/Nux.h>
+#include <NuxGraphics/CairoGraphics.h>
+
+#include "VScrollBarOverlayWindow.h"
+#include "UScreen.h"
+#include "DashStyle.h"
+#include "CairoTexture.h"
+
+namespace
+{
+ int const THUMB_WIDTH = 21;
+ int const THUMB_HEIGHT = 68;
+ int const THUMB_RADIUS = 3;
+}
+
+
+VScrollBarOverlayWindow::VScrollBarOverlayWindow(nux::Geometry const& geo)
+ : nux::BaseWindow("")
+ , content_size_(geo)
+ , content_offset_x_(0)
+ , mouse_offset_y_(0)
+ , mouse_down_(false)
+ , mouse_near_(false)
+ , inside_slider_(false)
+ , current_action_(ThumbAction::NONE)
+{
+ Area::SetGeometry(content_size_.x, content_size_.y, THUMB_WIDTH, content_size_.height);
+ SetBackgroundColor(nux::color::Transparent);
+
+ UpdateTexture();
+}
+
+void VScrollBarOverlayWindow::UpdateGeometry(nux::Geometry const& geo)
+{
+ if (content_size_.x != geo.x ||
+ content_size_.y != geo.y ||
+ content_size_.height != geo.height)
+ {
+ content_size_ = geo;
+ UpdateMouseOffsetX();
+
+ Area::SetGeometry(content_size_.x + content_offset_x_, content_size_.y, THUMB_WIDTH, content_size_.height);
+ }
+}
+
+void VScrollBarOverlayWindow::SetThumbOffsetY(int y)
+{
+ int const new_offset = GetValidOffsetYValue(y);
+
+ if (new_offset != mouse_offset_y_)
+ {
+ if (mouse_down_)
+ MouseDragging();
+
+ mouse_offset_y_ = new_offset;
+ QueueDraw();
+ }
+}
+
+int VScrollBarOverlayWindow::GetValidOffsetYValue(int new_offset) const
+{
+ if (new_offset < 0)
+ return 0;
+ else if (new_offset > content_size_.height - THUMB_HEIGHT)
+ return content_size_.height - THUMB_HEIGHT;
+
+ return new_offset;
+}
+
+void VScrollBarOverlayWindow::UpdateMouseOffsetX()
+{
+ int monitor = unity::UScreen::GetDefault()->GetMonitorWithMouse();
+ nux::Geometry const& geo = unity::UScreen::GetDefault()->GetMonitorGeometry(monitor);
+
+ if (content_size_.x + THUMB_WIDTH > geo.x + geo.width)
+ content_offset_x_ = geo.x + geo.width - (content_size_.x + THUMB_WIDTH);
+ else
+ content_offset_x_ = 0;
+}
+
+bool VScrollBarOverlayWindow::IsMouseInsideThumb(int y) const
+{
+ nux::Geometry const thumb(0, mouse_offset_y_, THUMB_WIDTH, THUMB_HEIGHT);
+ return thumb.IsPointInside(0,y);
+}
+
+bool VScrollBarOverlayWindow::IsMouseBeingDragged() const
+{
+ return current_action_ == ThumbAction::DRAGGING;
+}
+
+int VScrollBarOverlayWindow::GetThumbHeight() const
+{
+ return THUMB_HEIGHT;
+}
+
+int VScrollBarOverlayWindow::GetThumbOffsetY() const
+{
+ return mouse_offset_y_;
+}
+
+nux::Geometry VScrollBarOverlayWindow::GetThumbGeometry() const
+{
+ return nux::Geometry(content_size_.x + content_offset_x_,
+ content_size_.y + mouse_offset_y_,
+ THUMB_WIDTH, THUMB_HEIGHT);
+}
+
+void VScrollBarOverlayWindow::MouseDown()
+{
+ mouse_down_ = true;
+ UpdateTexture();
+}
+
+void VScrollBarOverlayWindow::MouseUp()
+{
+ mouse_down_ = false;
+ current_action_ = ThumbAction::NONE;
+ UpdateTexture();
+ ShouldHide();
+}
+
+void VScrollBarOverlayWindow::MouseNear()
+{
+ mouse_near_ = true;
+ ShouldShow();
+}
+
+void VScrollBarOverlayWindow::MouseBeyond()
+{
+ mouse_near_ = false;
+ ShouldHide();
+}
+
+void VScrollBarOverlayWindow::ThumbInsideSlider()
+{
+ if (!inside_slider_)
+ {
+ inside_slider_ = true;
+ UpdateTexture();
+ }
+}
+
+void VScrollBarOverlayWindow::ThumbOutsideSlider()
+{
+ if (inside_slider_)
+ {
+ inside_slider_ = false;
+ UpdateTexture();
+ }
+}
+
+void VScrollBarOverlayWindow::PageUpAction()
+{
+ current_action_ = ThumbAction::PAGE_UP;
+ UpdateTexture();
+}
+
+void VScrollBarOverlayWindow::PageDownAction()
+{
+ current_action_ = ThumbAction::PAGE_DOWN;
+ UpdateTexture();
+}
+
+void VScrollBarOverlayWindow::MouseDragging()
+{
+ if (current_action_ != ThumbAction::DRAGGING)
+ {
+ current_action_ = ThumbAction::DRAGGING;
+ UpdateTexture();
+ }
+}
+
+void VScrollBarOverlayWindow::ShouldShow()
+{
+ if (!IsVisible())
+ {
+ if (mouse_down_ || mouse_near_)
+ {
+ ShowWindow(true);
+ PushToFront();
+ QueueDraw();
+ }
+ }
+}
+
+void VScrollBarOverlayWindow::ShouldHide()
+{
+ if (IsVisible())
+ {
+ if (!mouse_down_ && !mouse_near_)
+ {
+ ShowWindow(false);
+ QueueDraw();
+ }
+ }
+}
+
+void VScrollBarOverlayWindow::ResetStates()
+{
+ mouse_down_ = false;
+ mouse_near_ = false;
+ current_action_ = ThumbAction::NONE;
+ ShouldHide();
+}
+
+void VScrollBarOverlayWindow::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw)
+{
+ if (!thumb_texture_)
+ return;
+
+ nux::Geometry base(0, mouse_offset_y_, THUMB_WIDTH, THUMB_HEIGHT);
+ nux::TexCoordXForm texxform;
+
+ graphics_engine.QRP_1Tex(base.x,
+ base.y,
+ base.width,
+ base.height,
+ thumb_texture_->GetDeviceTexture(),
+ texxform,
+ nux::color::White);
+}
+
+nux::color::RedGreenBlue ProduceColorShade(nux::color::RedGreenBlue const& rgb, float shade)
+{
+ if (shade == 1.0f)
+ return rgb;
+
+ nux::color::HueLightnessSaturation hls(rgb);
+
+ hls.lightness *= shade;
+ if (hls.lightness > 1.0f)
+ hls.lightness = 1.0f;
+ else if (hls.lightness < 0.0f)
+ hls.lightness = 0.0f;
+
+ hls.saturation *= shade;
+ if (hls.saturation > 1.0f)
+ hls.saturation = 1.0f;
+ else if (hls.saturation < 0.0f)
+ hls.saturation = 0.0f;
+
+ nux::color::RedGreenBlue rgb_shade(hls);
+
+ return rgb_shade;
+}
+
+void PatternAddRGBStop(cairo_pattern_t* pat, nux::color::RedGreenBlue const& rgb, double stop, float alpha)
+{
+ cairo_pattern_add_color_stop_rgba (pat, stop, rgb.red, rgb.green, rgb.blue, alpha);
+}
+
+void SetSourceRGB(cairo_t* cr, nux::color::RedGreenBlue const& rgb, float alpha)
+{
+ cairo_set_source_rgba(cr, rgb.red, rgb.green, rgb.blue, alpha);
+}
+
+void DrawGrip (cairo_t* cr, double x, double y, int nx, int ny)
+{
+ gint lx, ly;
+
+ for (ly = 0; ly < ny; ly++)
+ {
+ for (lx = 0; lx < nx; lx++)
+ {
+ gint sx = lx * 3;
+ gint sy = ly * 3;
+
+ cairo_rectangle (cr, x + sx, y + sy, 1, 1);
+ }
+ }
+}
+
+void DrawBothGrips(cairo_t* cr, nux::color::RedGreenBlue const& rgb, int width, int height)
+{
+ int const grip_width = 5;
+ int const grip_height = 6;
+ float const grip_y = 13.5;
+ float const offset = 6.5;
+
+ cairo_pattern_t* pat;
+ pat = cairo_pattern_create_linear(0, 0, 0, height);
+
+ PatternAddRGBStop(pat, rgb, 0.0, 0.0);
+ PatternAddRGBStop(pat, rgb, 0.49, 0.5);
+ PatternAddRGBStop(pat, rgb, 0.49, 0.5);
+ PatternAddRGBStop(pat, rgb, 1.0, 0.0);
+
+ cairo_set_source(cr, pat);
+ cairo_pattern_destroy(pat);
+
+ DrawGrip(cr, width/2 - offset, grip_y, grip_width, grip_height);
+ DrawGrip(cr, width/2 - offset, height/2 + (grip_y - 10), grip_width, grip_height);
+
+ cairo_fill(cr);
+}
+
+void DrawLineSeperator(cairo_t* cr, nux::color::RedGreenBlue const& top,
+ nux::color::RedGreenBlue const& bottom, int width, int height)
+{
+ int const offset = 1.5;
+
+ // Top
+ cairo_move_to(cr, offset, height/2);
+ cairo_line_to(cr, width - offset, height/2);
+ SetSourceRGB(cr, top, 0.36);
+ cairo_stroke(cr);
+
+ // Bottom
+ cairo_move_to(cr, offset, 1 + height/2);
+ cairo_line_to(cr, width - offset, 1 + height/2);
+ SetSourceRGB(cr, bottom, 0.5);
+ cairo_stroke(cr);
+}
+
+
+void DrawArrow (cairo_t* cr, nux::color::RedGreenBlue const& rgb, double x, double y, double width, double height)
+{
+ cairo_save (cr);
+
+ cairo_translate (cr, x, y);
+ cairo_move_to (cr, -width / 2, -height / 2);
+ cairo_line_to (cr, 0, height / 2);
+ cairo_line_to (cr, width / 2, -height / 2);
+ cairo_close_path (cr);
+
+ SetSourceRGB(cr, rgb, 0.75);
+ cairo_fill_preserve (cr);
+
+ SetSourceRGB(cr, rgb, 1.0);
+ cairo_stroke (cr);
+
+ cairo_restore (cr);
+}
+
+void DrawBothArrows(cairo_t* cr, nux::color::RedGreenBlue const& rgb, int width, int height)
+{
+ int const arrow_width = 5;
+ int const arrow_height = 3;
+ float const trans_height = 8.5;
+ float const offset_x = 0.5;
+
+ // Top
+ cairo_save(cr);
+ cairo_translate(cr, width/2 + offset_x, trans_height);
+ cairo_rotate(cr, G_PI);
+ DrawArrow(cr, rgb, offset_x, 0, arrow_width, arrow_height);
+ cairo_restore(cr);
+
+ // Bottom
+ cairo_save(cr);
+ cairo_translate(cr, width/2 + offset_x, height - trans_height);
+ cairo_rotate(cr, 0);
+ DrawArrow(cr, rgb, -offset_x, 0, arrow_width, arrow_height);
+ cairo_restore(cr);
+}
+
+void VScrollBarOverlayWindow::UpdateTexture()
+{
+ int width = THUMB_WIDTH;
+ int height = THUMB_HEIGHT;
+ int radius = THUMB_RADIUS;
+
+ float const aspect = 1.0f;
+ float current_x = 0.0f;
+ float current_y = 0.0f;
+
+ cairo_t* cr = NULL;
+ cairo_pattern_t* pat = NULL;
+
+ nux::color::RedGreenBlue const& bg = nux::color::WhiteSmoke;
+ nux::color::RedGreenBlue const& bg_selected = nux::color::White;
+ nux::color::RedGreenBlue const& bg_active = nux::color::Gray;
+ nux::color::RedGreenBlue const& arrow_color = nux::color::DarkSlateGray;
+
+ nux::color::RedGreenBlue const& bg_arrow_up = ProduceColorShade(bg, 0.86);
+ nux::color::RedGreenBlue const& bg_arrow_down = ProduceColorShade(bg, 1.1);
+ nux::color::RedGreenBlue const& bg_shadow = ProduceColorShade(bg, 0.2);
+
+ nux::color::RedGreenBlue const& bg_dark_line = ProduceColorShade(bg, 0.4);
+ nux::color::RedGreenBlue const& bg_bright_line = ProduceColorShade(bg, 1.2);
+
+ nux::CairoGraphics cairoGraphics(CAIRO_FORMAT_ARGB32, width, height);
+ cr = cairoGraphics.GetContext();
+
+ cairo_save(cr);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(cr);
+
+ cairo_save(cr);
+
+ cairo_translate (cr, 0.5, 0.5);
+ width--;
+ height--;
+
+ cairo_set_line_width (cr, 1.0);
+
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_save(cr);
+
+ // Draw backgound
+ SetSourceRGB(cr, bg, 1.0);
+ cairoGraphics.DrawRoundedRectangle(cr, aspect, current_x, current_y, radius, width, height);
+ cairo_fill_preserve(cr);
+
+ // Draw shaded background
+ pat = cairo_pattern_create_linear(0, 0, 0, height);
+
+ PatternAddRGBStop(pat, bg_arrow_up, 0.0, 0.8);
+ PatternAddRGBStop(pat, bg_arrow_down, 1.0, 0.8);
+
+ cairo_set_source(cr, pat);
+ cairo_pattern_destroy(pat);
+
+ if (current_action_ == ThumbAction::DRAGGING)
+ {
+ cairo_fill_preserve(cr);
+ SetSourceRGB(cr, bg, 0.8);
+ cairo_fill(cr);
+ }
+ else
+ {
+ cairo_fill(cr);
+ }
+
+ // Draw Page Up/Down Action
+ if (current_action_ == ThumbAction::PAGE_UP ||
+ current_action_ == ThumbAction::PAGE_DOWN)
+ {
+ if (current_action_ == ThumbAction::PAGE_UP)
+ cairo_rectangle(cr, 0, 0, width, height/2);
+ else
+ cairo_rectangle(cr, 0, height/2, width, height/2);
+
+ SetSourceRGB(cr, bg, 0.8);
+ cairo_fill(cr);
+ }
+
+ cairo_save(cr);
+
+ // Draw Outline
+ cairo_set_line_width (cr, 2.0);
+
+ current_x += 0.5;
+ current_y += 0.5;
+ cairoGraphics.DrawRoundedRectangle(cr, aspect, current_x, current_y, radius - 1, width - 1, height - 1);
+
+ if (inside_slider_)
+ SetSourceRGB(cr, bg_selected, 1.0);
+ else
+ SetSourceRGB(cr, bg_active, 0.9);
+
+ cairo_stroke(cr);
+
+ cairo_restore(cr);
+
+ // Draw shade outline
+ pat = cairo_pattern_create_linear(0, 0, 0, height);
+
+ PatternAddRGBStop(pat, bg_shadow, 0.5, 0.06);
+
+ switch(current_action_)
+ {
+ case ThumbAction::NONE:
+ PatternAddRGBStop(pat, bg_shadow, 0.0, 0.22);
+ PatternAddRGBStop(pat, bg_shadow, 1.0, 0.22);
+ break;
+ case ThumbAction::DRAGGING:
+ PatternAddRGBStop(pat, bg_shadow, 0.0, 0.2);
+ PatternAddRGBStop(pat, bg_shadow, 1.0, 0.2);
+ break;
+ case ThumbAction::PAGE_UP:
+ PatternAddRGBStop(pat, bg_shadow, 0.0, 0.1);
+ PatternAddRGBStop(pat, bg_shadow, 1.0, 0.22);
+ break;
+ case ThumbAction::PAGE_DOWN:
+ PatternAddRGBStop(pat, bg_shadow, 0.0, 0.22);
+ PatternAddRGBStop(pat, bg_shadow, 1.0, 0.1);
+ break;
+ default:
+ break;
+ }
+
+ cairo_set_source(cr, pat);
+ cairo_pattern_destroy(pat);
+
+ current_x += 0.5;
+ current_y += 0.5;
+ cairoGraphics.DrawRoundedRectangle(cr, aspect, current_x, current_y, radius, width- 2, height - 2);
+ cairo_stroke(cr);
+
+ current_x += 1.0;
+ current_y += 1.0;
+ cairoGraphics.DrawRoundedRectangle(cr, aspect, current_x, current_y, radius - 1, width - 4, height- 4);
+ SetSourceRGB(cr, bg_bright_line, 0.6);
+ cairo_stroke(cr);
+
+ DrawBothGrips(cr, bg_dark_line, width, height);
+ DrawLineSeperator(cr, bg_dark_line, bg_bright_line, width, height);
+ DrawBothArrows(cr, arrow_color, width, height);
+
+ thumb_texture_.Adopt(unity::texture_from_cairo_graphics(cairoGraphics));
+ cairo_destroy(cr);
+
+ QueueDraw();
+}
diff --git a/unity-shared/VScrollBarOverlayWindow.h b/unity-shared/VScrollBarOverlayWindow.h
new file mode 100644
index 000000000..abcf6db25
--- /dev/null
+++ b/unity-shared/VScrollBarOverlayWindow.h
@@ -0,0 +1,92 @@
+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
+/*
+ * Copyright (C) 2012 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Brandon Schaefer <brandon.schaefer@canonical.com>
+ */
+
+
+#ifndef VSCROLLBAR_OVERLAY_WINDOW_H
+#define VSCROLLBAR_OVERLAY_WINDOW_H
+
+#include <Nux/Nux.h>
+#include <Nux/BaseWindow.h>
+
+
+class VScrollBarOverlayWindow : public nux::BaseWindow
+{
+public:
+ VScrollBarOverlayWindow(nux::Geometry const& geo);
+
+ void UpdateGeometry(nux::Geometry const& geo);
+ void SetThumbOffsetY(int y);
+
+ void MouseDown();
+ void MouseUp();
+
+ void MouseNear();
+ void MouseBeyond();
+
+ void ThumbInsideSlider();
+ void ThumbOutsideSlider();
+
+ void PageUpAction();
+ void PageDownAction();
+
+ bool IsMouseInsideThumb(int y) const;
+ bool IsMouseBeingDragged() const;
+
+ int GetThumbHeight() const;
+ int GetThumbOffsetY() const;
+
+ nux::Geometry GetThumbGeometry() const;
+
+ void ResetStates();
+
+protected:
+ virtual void Draw(nux::GraphicsEngine& graphics_engine, bool force_draw);
+
+private:
+ enum class ThumbAction : unsigned int
+ {
+ NONE,
+ DRAGGING,
+ PAGE_UP,
+ PAGE_DOWN
+ };
+
+ void MouseDragging();
+ void UpdateMouseOffsetX();
+ int GetValidOffsetYValue(int y) const;
+
+ void ShouldShow();
+ void ShouldHide();
+
+ void UpdateTexture();
+
+ nux::Geometry content_size_;
+ nux::ObjectPtr<nux::BaseTexture> thumb_texture_;
+
+ int content_offset_x_;
+ int mouse_offset_y_;
+
+ bool mouse_down_;
+ bool mouse_near_;
+ bool inside_slider_;
+
+ ThumbAction current_action_;
+};
+
+#endif