diff options
| -rw-r--r-- | dash/DashView.cpp | 13 | ||||
| -rwxr-xr-x | dash/LensView.cpp | 16 | ||||
| -rw-r--r-- | dash/previews/ApplicationPreview.cpp | 4 | ||||
| -rw-r--r-- | dash/previews/GenericPreview.cpp | 4 | ||||
| -rw-r--r-- | dash/previews/MoviePreview.cpp | 4 | ||||
| -rw-r--r-- | dash/previews/SocialPreview.cpp | 4 | ||||
| -rw-r--r-- | dash/previews/Tracks.cpp | 4 | ||||
| -rw-r--r-- | tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/test_overlay_scrollbar.cpp | 280 | ||||
| -rw-r--r-- | unity-shared/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | unity-shared/PlacesOverlayVScrollBar.cpp | 417 | ||||
| -rw-r--r-- | unity-shared/PlacesOverlayVScrollBar.h | 107 | ||||
| -rw-r--r-- | unity-shared/VScrollBarOverlayWindow.cpp | 526 | ||||
| -rw-r--r-- | unity-shared/VScrollBarOverlayWindow.h | 92 |
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 |
