diff options
| author | Rudra Saraswat <rs2009@ubuntu.com> | 2023-02-22 18:37:59 +0000 |
|---|---|---|
| committer | Bileto Bot <ci-train-bot@canonical.com> | 2023-02-22 18:37:59 +0000 |
| commit | 4bc29d53573b0943a77f56772fb0f4b183192cad (patch) | |
| tree | 89a975ff9b07e1c9e594bf633f34d34765d79b8e | |
| parent | cd0e7d8f64a348def9f05853921f76737157db78 (diff) | |
| parent | 9e2b1125105ee5deb6291c1046491795e59d0a6b (diff) | |
* Added UWidgets, a new widget system for Unity based on Blighty
* Made the dash vertical and moved the scope bar to the top * Increased the panel height to 30 * Added indicator-notifications as a Recommend
99 files changed, 3916 insertions, 2484 deletions
diff --git a/.gitignore b/.gitignore index d58747cbb..da7cffb1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,90 @@ build -services/panel-marshal.c -services/panel-marshal.h .AUTHORS.sed -tests/autopilot/dist -tests/autopilot/unity.egg-info *.log *.debhelper *.substvars *.swp +.vscode/ debian/tmp debian/files obj-*/ -debian/libunity-core-*/ -debian/unity-*/ +debian/libunity-core-6.0-9/ +debian/libunity-core-6.0-dev/ +debian/unity-autopilot/ +debian/unity-uwidgets/ +debian/unity-services/ +debian/unity-schemas/ debian/unity/ + +### UWidgets + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Sphinx documentation +docs/*build/ + +# PyBuilder +target/ + +# pyenv +.python-version + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d58a4619..bd237ca47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ include (GNUInstallDirs) # set (PROJECT_NAME "unity") set (UNITY_MAJOR 7) -set (UNITY_MINOR 6) +set (UNITY_MINOR 7) set (UNITY_MICRO 0) set (UNITY_VERSION "${UNITY_MAJOR}.${UNITY_MINOR}.${UNITY_MICRO}") set (UNITY_API_VERSION "6.0") @@ -19,3 +19,7 @@ D-Bus activation. This is used for testing how the panel reacts when it starts before the service does. + Credits +-------------------------------------------------------------------------------- + +UWidgets is based on the Blighty project. \ No newline at end of file diff --git a/dash/DashView.cpp b/dash/DashView.cpp index dc4e1b2d8..0fd05974d 100644 --- a/dash/DashView.cpp +++ b/dash/DashView.cpp @@ -39,6 +39,7 @@ #include "unity-shared/UBusMessages.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/WindowManager.h" +#include "unity-shared/FileManager.h" namespace unity { @@ -231,83 +232,12 @@ void DashView::OnResultActivated(ResultView::ActivateType type, LocalResult cons void DashView::BuildPreview(Preview::Ptr model) { - if (!preview_displaying_) - { - StartPreviewAnimation(); - - content_view_->SetPresentRedirectedView(false); - preview_scope_view_ = active_scope_view_; - if (preview_scope_view_) - { - preview_scope_view_->ForceCategoryExpansion(stored_activated_unique_id_, true); - preview_scope_view_->EnableResultTextures(true); - preview_scope_view_->PushFilterExpansion(false); - } - - if (!preview_container_) - { - preview_container_ = new previews::PreviewContainer(); - preview_container_->SetRedirectRenderingToTexture(true); - AddChild(preview_container_.GetPointer()); - preview_container_->SetParentObject(this); - } - preview_container_->Preview(model, previews::Navigation::NONE); // no swipe left or right - preview_container_->scale = scale(); - preview_container_->SetGeometry(scopes_layout_->GetGeometry()); - preview_displaying_ = true; - - // connect to nav left/right signals to request nav left/right movement. - preview_container_->navigate_left.connect([this] () { - preview_navigation_mode_ = previews::Navigation::LEFT; - - // sends a message to all result views, sending the the uri of the current preview result - // and the unique id of the result view that should be handling the results - ubus_manager_.SendMessage(UBUS_DASH_PREVIEW_NAVIGATION_REQUEST, g_variant_new("(ivs)", -1, g_variant_ref(last_activated_result_.Variant()), stored_activated_unique_id_.c_str())); - }); - - preview_container_->navigate_right.connect([this] () { - preview_navigation_mode_ = previews::Navigation::RIGHT; - - // sends a message to all result views, sending the the uri of the current preview result - // and the unique id of the result view that should be handling the results - ubus_manager_.SendMessage(UBUS_DASH_PREVIEW_NAVIGATION_REQUEST, g_variant_new("(ivs)", 1, g_variant_ref(last_activated_result_.Variant()), stored_activated_unique_id_.c_str())); - }); - - preview_container_->request_close.connect([this] () { ClosePreview(); }); - } - else - { - // got a new preview whilst already displaying, we probably clicked a navigation button. - preview_container_->Preview(model, preview_navigation_mode_); // TODO - preview_container_->scale = scale(); - } - - if (G_LIKELY(preview_state_machine_.left_results() > 0 && preview_state_machine_.right_results() > 0)) - preview_container_->DisableNavButton(previews::Navigation::NONE); - else if (preview_state_machine_.left_results() > 0) - preview_container_->DisableNavButton(previews::Navigation::RIGHT); - else if (preview_state_machine_.right_results() > 0) - preview_container_->DisableNavButton(previews::Navigation::LEFT); - else - preview_container_->DisableNavButton(previews::Navigation::BOTH); - - QueueDraw(); + // Previews have been removed, so this function has been left blank until we eliminate all of the preview-related code. } void DashView::ClosePreview() { - if (preview_displaying_) - { - EndPreviewAnimation(); - - preview_displaying_ = false; - } - - preview_navigation_mode_ = previews::Navigation::NONE; - - // re-focus dash view component. - nux::GetWindowCompositor().SetKeyFocusArea(default_focus()); - QueueDraw(); + // Previews have been removed, so this function has been left blank until we eliminate all of the preview-related code. } void DashView::StartPreviewAnimation() @@ -555,6 +485,11 @@ void DashView::SetupViews() content_view_->SetLayout(content_layout_); layout_->AddView(content_view_, 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); + scope_bar_ = new ScopeBar(); + AddChild(scope_bar_); + scope_bar_->scope_activated.connect(sigc::mem_fun(this, &DashView::OnScopeBarActivated)); + content_layout_->AddView(scope_bar_, 0, nux::MINOR_POSITION_CENTER); + search_bar_layout_ = new nux::HLayout(); content_layout_->AddLayout(search_bar_layout_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); @@ -578,11 +513,6 @@ void DashView::SetupViews() scopes_layout_ = new nux::VLayout(); content_layout_->AddLayout(scopes_layout_, 1, nux::MINOR_POSITION_START); - scope_bar_ = new ScopeBar(); - AddChild(scope_bar_); - scope_bar_->scope_activated.connect(sigc::mem_fun(this, &DashView::OnScopeBarActivated)); - content_layout_->AddView(scope_bar_, 0, nux::MINOR_POSITION_CENTER); - OnDPIChanged(); } @@ -701,24 +631,24 @@ nux::Geometry DashView::GetBestFitGeometry(nux::Geometry const& for_geo) style.GetPlacesGroupResultTopPadding().CP(scale) + style.GetTileHeight().CP(scale)); - int half = for_geo.width / 2; - - // if default dash size is bigger than half a screens worth of items, go for that. - while ((width += tile_width) < half); + // let's show five tiles per row + width = tile_width * 5; - width = std::max(width, tile_width * DASH_TILE_HORIZONTAL_COUNT); width += style.GetVSeparatorSize().CP(scale); width += style.GetPlacesGroupResultLeftPadding().CP(scale) + DASH_RESULT_RIGHT_PAD.CP(scale); - height = style.GetHSeparatorSize().CP(scale); - height += style.GetDashViewTopPadding().CP(scale); - height += search_bar_->GetGeometry().height; - height += category_height * DASH_DEFAULT_CATEGORY_COUNT; // adding three categories - height += scope_bar_->GetGeometry().height; - - // width/height shouldn't be bigger than the geo available. + // width shouldn't be bigger than the geo available. width = std::min(width, for_geo.width); // launcher width is taken into account in for_geo. - height = std::min(height, for_geo.height - vertical_offset); // panel height is not taken into account in for_geo. + height = for_geo.height - vertical_offset; + + if (Settings::Instance().launcher_position() == LauncherPosition::BOTTOM) { + height = style.GetHSeparatorSize().CP(scale); + height += style.GetDashViewTopPadding().CP(scale); + height += search_bar_->GetGeometry().height; + height += category_height * 3; + height += scope_bar_->GetGeometry().height; + height = std::min(height, for_geo.height - vertical_offset); + } return nux::Geometry(0, vertical_offset, width, height); } @@ -1091,59 +1021,7 @@ void DashView::DrawPreviewResultTextures(nux::GraphicsEngine& graphics_engine, b } void DashView::DrawPreview(nux::GraphicsEngine& graphics_engine, bool force_draw) -{ - if (animate_preview_value_ > 0.0f) - { - bool animating = animate_split_value_ != 1.0f || animate_preview_value_ < 1.0f; - bool preview_force_draw = force_draw || animating || IsFullRedraw(); - - if (preview_force_draw) - nux::GetPainter().PushBackgroundStack(); - - if (animate_preview_value_ < 1.0f && preview_container_->RedirectRenderingToTexture()) - { - preview_container_->SetPresentRedirectedView(false); - preview_container_->ProcessDraw(graphics_engine, preview_force_draw); - - unsigned int alpha, src, dest = 0; - graphics_engine.GetRenderStates().GetBlend(alpha, src, dest); - graphics_engine.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - nux::ObjectPtr<nux::IOpenGLBaseTexture> preview_texture = preview_container_->BackupTexture(); - if (preview_texture) - { - nux::TexCoordXForm texxform; - texxform.FlipVCoord(true); - texxform.uoffset = 0.0f; - texxform.voffset = 0.0f; - texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); - - nux::Geometry const& geo_preview = preview_container_->GetGeometry(); - graphics_engine.QRP_1Tex - ( - geo_preview.x, - geo_preview.y, - geo_preview.width, - geo_preview.height, - preview_texture, - texxform, - nux::Color(animate_preview_value_, animate_preview_value_, animate_preview_value_, animate_preview_value_) - ); - } - - preview_container_->SetPresentRedirectedView(true); - - graphics_engine.GetRenderStates().SetBlend(alpha, src, dest); - } - else - { - preview_container_->ProcessDraw(graphics_engine, preview_force_draw); - } - - if (preview_force_draw) - nux::GetPainter().PopBackgroundStack(); - } -} +{ } void DashView::OnActivateRequest(GVariant* args) { diff --git a/dash/PlacesGroup.cpp b/dash/PlacesGroup.cpp index 7a1f8dbf0..b83be9b34 100755 --- a/dash/PlacesGroup.cpp +++ b/dash/PlacesGroup.cpp @@ -52,6 +52,9 @@ namespace const nux::Color EXPAND_DEFAULT_TEXT_COLOR(1.0f, 1.0f, 1.0f, 0.5f); const float EXPAND_DEFAULT_ICON_OPACITY = 0.5f; +// Category heading +const RawPixel HEADER_VERTICAL_MARGIN = 10_em; + // Category highlight const RawPixel HIGHLIGHT_RIGHT_PADDING = 10_em; const RawPixel HIGHLIGHT_HEIGHT = 24_em; @@ -229,6 +232,7 @@ PlacesGroup::UpdatePlacesGroupSize() _header_layout->SetSpaceBetweenChildren(SPACE_BETWEEN_CHILDREN.CP(scale())); _header_layout->SetLeftAndRightPadding(_style.GetCategoryHeaderLeftPadding().CP(scale), 0); + _header_layout->SetVerticalExternalMargin(HEADER_VERTICAL_MARGIN); _icon->SetMinMaxSize(icon_size, icon_size); diff --git a/dash/ResultViewGrid.cpp b/dash/ResultViewGrid.cpp index 78a01de9a..16692607b 100644 --- a/dash/ResultViewGrid.cpp +++ b/dash/ResultViewGrid.cpp @@ -55,7 +55,7 @@ namespace const int DOUBLE_CLICK_SPEED = 500; //500 ms (double-click speed hardcoded to 400 ms in nux) - const RawPixel WIDTH_PADDING = 25_em; + const RawPixel WIDTH_PADDING = 25_em; const RawPixel SCROLLBAR_WIDTH = 3_em; const std::string APPLICATION_URI_PREFIX = "application://"; diff --git a/dash/ScopeBar.cpp b/dash/ScopeBar.cpp index 027675c5a..5bba47719 100644 --- a/dash/ScopeBar.cpp +++ b/dash/ScopeBar.cpp @@ -58,7 +58,7 @@ void ScopeBar::SetupBackground() rop.Blend = true; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; - bg_layer_.reset(new nux::ColorLayer(nux::Color(0.0f, 0.0f, 0.0f, 0.2f), true, rop)); + bg_layer_.reset(new nux::ColorLayer(nux::Color(0.0f, 0.0f, 0.0f, 0.0f), true, rop)); } void ScopeBar::UpdateScale(double scale) diff --git a/dash/StandaloneDash.cpp b/dash/StandaloneDash.cpp index e869aced4..b14d36040 100644 --- a/dash/StandaloneDash.cpp +++ b/dash/StandaloneDash.cpp @@ -41,8 +41,8 @@ #include <UnityCore/GSettingsScopes.h> #include <UnityCore/ScopeProxyInterface.h> -const unity::RawPixel WIDTH(1024); -const unity::RawPixel HEIGHT(768); +const unity::RawPixel WIDTH(1280); +const unity::RawPixel HEIGHT(720); using namespace unity::dash; @@ -126,7 +126,7 @@ int main(int argc, char **argv) 0, &TestRunner::InitWindowThread, test_runner)); nux::ObjectPtr<nux::BaseTexture> background_tex; - background_tex.Adopt(nux::CreateTextureFromFile("/usr/share/backgrounds/warty-final-ubuntu.png")); + background_tex.Adopt(nux::CreateTextureFromFile("/usr/share/backgrounds/ubuntu-unity/ubuntu-unity-default.png")); nux::TexCoordXForm texxform; auto tex_layer = std::make_shared<nux::TextureLayer>(background_tex->GetDeviceTexture(), texxform, nux::color::White); wt->SetWindowBackgroundPaintLayer(tex_layer.get()); diff --git a/debian/changelog b/debian/changelog index a3a4d5d4d..1767d6dcd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +unity (7.7.0+23.04.20230222-0ubuntu1) lunar; urgency=medium + + * Added UWidgets, a new widget system for Unity based on Blighty + * Made the dash vertical and moved the scope bar to the top + * Increased the panel height to 30 + * Added indicator-notifications as a Recommend + + -- Rudra Saraswat <ruds@ruds.io> Tue, 21 Feb 2022 15:38:23 +0530 + unity (7.6.0+22.10.20220913-0ubuntu1) kinetic; urgency=medium [ Rudra Saraswat ] diff --git a/debian/control b/debian/control index c61edb5fe..2484f2227 100644 --- a/debian/control +++ b/debian/control @@ -11,6 +11,8 @@ Build-Depends: cmake (>= 3.17.0), dh-translations (>= 94), dh-python, google-mock (>= 1.6.0+svn437), + gir1.2-gtk-3.0, + gir1.2-glib-2.0, gsettings-desktop-schemas-dev, gsettings-ubuntu-schemas (>= 0.0.7+17.10.20170922), intltool (>= 0.35.0), @@ -48,17 +50,21 @@ Build-Depends: cmake (>= 3.17.0), libxcb-icccm4-dev, libxfixes-dev (>= 1:5.0.1), libxi-dev (>= 2:1.7.1.901), + libxinerama-dev, libxpathselect-dev (>=1.4), libxtst-dev, libzeitgeist-2.0-dev, pkg-config, python3-all (>= 3.4), + python3-dev, + python3-gi, python3-setuptools, quilt, systemd, + xorg, xserver-xorg-video-dummy, xsltproc, - yaru-theme-icon, + yaru-theme-icon Standards-Version: 3.9.5 Homepage: https://launchpad.net/unity # If you aren't a member of ~unity-team but need to upload packaging changes, @@ -95,6 +101,8 @@ Recommends: unity-control-center, hud, session-shortcuts, unity-session, + unity-uwidgets, + indicator-notifications Breaks: unity-lens-applications (<< 5.12.0-0ubuntu2), unity-lens-files (<< 5.10.0-0ubuntu2), unity-lens-music (<< 6.0.0), @@ -220,3 +228,28 @@ Description: Autopiloted tests for Unity bindings to be able to write tests in python as well as the full test suite for Unity. +Package: unity-uwidgets +Section: python +Architecture: all +Depends: ${misc:Depends}, + ${python3:Depends}, + python3, + xorg, + libxinerama-dev, + libcairo2-dev, + python3-gi, + python3-pil, + python3-pydbus, + python3-psutil, + gir1.2-gtk-3.0, + gir1.2-glib-2.0, +Description: Widgets for Unity + Unity is a desktop experience that sings. Designed by Canonical and the Ayatana + community, Unity is all about the combination of familiarity and the future. We + bring together visual design, analysis of user experience testing, modern + graphics technologies and a deep understanding of the free software landscape + to produce what we hope will be the lightest, most elegant and most delightful + way to use your PC. + . + This package contains support for widgets for Unity, based on Blighty. + diff --git a/debian/rules b/debian/rules index 4b29c43fb..038698489 100755 --- a/debian/rules +++ b/debian/rules @@ -35,13 +35,12 @@ override_dh_auto_configure: override_dh_install: # install autopilot tests - cd tests/autopilot; \ - set -ex; for python in $(shell py3versions -r); do \ - $$python setup.py install --root=$(CURDIR)/debian/tmp --install-layout=deb; \ - done; \ - cd $(CURDIR) + cd tests/autopilot; python3 setup.py install --root=$(CURDIR)/debian/tmp --install-layout=deb; find debian/tmp/usr/lib -name \*.*a -exec rm {} \; rm -rf debian/tmp/usr/share/gconf/schemas + # install uwidgets + cd uwidgets; python3 setup.py install --root=$(CURDIR)/debian/tmp --install-layout=deb + install -Dm755 uwidgets/uwidgets-runner.desktop $(CURDIR)/debian/tmp/etc/xdg/autostart/uwidgets-runner.desktop dh_install --fail-missing override_dh_gencontrol: diff --git a/debian/unity-uwidgets.install b/debian/unity-uwidgets.install new file mode 100644 index 000000000..5bbbd8873 --- /dev/null +++ b/debian/unity-uwidgets.install @@ -0,0 +1,3 @@ +usr/lib/python*/*/uwidgets* +usr/bin/uwidgets-runner +etc/xdg/autostart/uwidgets-runner.desktop \ No newline at end of file diff --git a/debian/unity.install b/debian/unity.install index b60c76294..224959ec9 100644 --- a/debian/unity.install +++ b/debian/unity.install @@ -1,6 +1,6 @@ etc/compizconfig etc/pam.d -usr/bin +usr/bin/unity usr/lib/*/compiz/libunity*.so usr/lib/*/unity/unity-active-plugins-safety-check usr/lib/*/unity/compiz-config-profile-setter diff --git a/launcher/BFBLauncherIcon.cpp b/launcher/BFBLauncherIcon.cpp index 68ff72dad..eece73a88 100644 --- a/launcher/BFBLauncherIcon.cpp +++ b/launcher/BFBLauncherIcon.cpp @@ -143,9 +143,7 @@ void BFBLauncherIcon::UpdateDefaultSearchText() { auto home_scope = reader_->GetScopeDataById("home.scope"); - tooltip_text = ((Settings::Instance().remote_content) ? - _("Search your computer and online sources") : - _("Search your computer")); + tooltip_text = _("Search your computer"); if (home_scope) { diff --git a/launcher/FileManagerLauncherIcon.cpp b/launcher/FileManagerLauncherIcon.cpp index 019c5507e..7e1560a95 100644 --- a/launcher/FileManagerLauncherIcon.cpp +++ b/launcher/FileManagerLauncherIcon.cpp @@ -23,7 +23,7 @@ #include <NuxCore/Logger.h> #include <UnityCore/DesktopUtilities.h> -#include "unity-shared/FileManager.h" +#include "unity-shared/GnomeFileManager.h" namespace unity { diff --git a/launcher/Launcher.cpp b/launcher/Launcher.cpp index e744757b2..a4f880af0 100644 --- a/launcher/Launcher.cpp +++ b/launcher/Launcher.cpp @@ -562,10 +562,8 @@ void Launcher::SetupRenderArg(AbstractLauncherIcon::Ptr const& icon, RenderArg& arg.progress_bias = IconProgressBias(icon); arg.progress = CLAMP(icon->GetProgress(), 0.0f, 1.0f); arg.draw_shortcut = shortcuts_shown_ && !hide_machine_.GetQuirk(LauncherHideMachine::PLACES_VISIBLE); - arg.system_item = icon->GetIconType() == AbstractLauncherIcon::IconType::HOME || - icon->GetIconType() == AbstractLauncherIcon::IconType::HUD; - arg.colorify_background = icon->GetIconType() == AbstractLauncherIcon::IconType::HOME || - icon->GetIconType() == AbstractLauncherIcon::IconType::HUD || + arg.system_item = icon->GetIconType() == AbstractLauncherIcon::IconType::HUD; + arg.colorify_background = icon->GetIconType() == AbstractLauncherIcon::IconType::HUD || icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH || icon->GetIconType() == AbstractLauncherIcon::IconType::DESKTOP || icon->GetIconType() == AbstractLauncherIcon::IconType::DEVICE || @@ -1113,13 +1111,6 @@ void Launcher::OnOverlayShown(GVariant* data) bg_effect_helper_.enabled = true; - // Don't desaturate icons if the mouse is over the launcher: - if (!hovered_) - { - LOG_DEBUG(logger) << "Desaturate on monitor " << monitor(); - DesaturateIcons(); - } - if (icon_under_mouse_) icon_under_mouse_->HideTooltip(); } @@ -2769,242 +2760,22 @@ bool Launcher::DndIsSpecialRequest(std::string const& uri) const } void Launcher::ProcessDndEnter() -{ -#ifdef USE_X11 - SetStateMouseOverLauncher(true); - - dnd_data_.Reset(); - drag_action_ = nux::DNDACTION_NONE; - steal_drag_ = false; - data_checked_ = false; - dnd_hovered_icon_ = nullptr; - drag_edge_touching_ = false; - dnd_hide_animation_.Stop(); -#endif -} +{ } void Launcher::DndReset() -{ -#ifdef USE_X11 - dnd_data_.Reset(); - - bool is_overlay_open = IsOverlayOpen(); - - for (auto it : *model_) - { - auto icon_type = it->GetIconType(); - bool desaturate = false; - - if (icon_type != AbstractLauncherIcon::IconType::HOME && - icon_type != AbstractLauncherIcon::IconType::HUD) - { - desaturate = is_overlay_open && !hovered_; - } - - it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, desaturate, monitor()); - it->SetQuirk(AbstractLauncherIcon::Quirk::UNFOLDED, false, monitor()); - } - - DndHoveredIconReset(); -#endif -} +{ } void Launcher::DndHoveredIconReset() -{ -#ifdef USE_X11 - SetActionState(ACTION_NONE); - - if (steal_drag_ && dnd_hovered_icon_) - { - dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false, monitor()); - dnd_hovered_icon_->remove.emit(dnd_hovered_icon_); - } - - if (!steal_drag_ && dnd_hovered_icon_) - { - dnd_hovered_icon_->SendDndLeave(); - dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, false, monitor()); - } - - steal_drag_ = false; - drag_edge_touching_ = false; - dnd_hovered_icon_ = nullptr; -#endif -} +{ } void Launcher::ProcessDndLeave() -{ -#ifdef USE_X11 - SetStateMouseOverLauncher(false); - DndHoveredIconReset(); -#endif -} +{ } void Launcher::ProcessDndMove(int x, int y, std::list<char*> mimes) -{ -#ifdef USE_X11 - if (!data_checked_) - { - const std::string uri_list = "text/uri-list"; - data_checked_ = true; - dnd_data_.Reset(); - auto& display = nux::GetWindowThread()->GetGraphicsDisplay(); - - // get the data - for (auto const& mime : mimes) - { - if (mime != uri_list) - continue; - - dnd_data_.Fill(display.GetDndData(const_cast<char*>(uri_list.c_str()))); - break; - } - - // see if the launcher wants this one - auto const& uris = dnd_data_.Uris(); - if (std::find_if(uris.begin(), uris.end(), [this] (std::string const& uri) - {return DndIsSpecialRequest(uri);}) != uris.end()) - { - steal_drag_ = true; - } - - // only set hover once we know our first x/y - SetActionState(ACTION_DRAG_EXTERNAL); - SetStateMouseOverLauncher(true); - } - - SetMousePosition(x - parent_->GetGeometry().x, y - parent_->GetGeometry().y); - - if (options()->hide_mode != LAUNCHER_HIDE_NEVER) - { - if ((monitor() == 0 && !IsOverlayOpen() && mouse_position_.x == 0 && !drag_edge_touching_) && - ((launcher_position_ == LauncherPosition::LEFT && - mouse_position_.y <= (parent_->GetGeometry().height - icon_size_.CP(cv_) - 2 * SPACE_BETWEEN_ICONS.CP(cv_))) || - (launcher_position_ == LauncherPosition::BOTTOM && - mouse_position_.x <= (parent_->GetGeometry().width - icon_size_.CP(cv_) - 2 * SPACE_BETWEEN_ICONS.CP(cv_))))) - { - if (dnd_hovered_icon_) - { - dnd_hovered_icon_->SendDndLeave(); - dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, false, monitor()); - } - - animation::StartOrReverse(dnd_hide_animation_, animation::Direction::FORWARD); - drag_edge_touching_ = true; - } - else if (drag_edge_touching_ && - ((launcher_position_ == LauncherPosition::LEFT && mouse_position_.x != 0) || - (launcher_position_ == LauncherPosition::BOTTOM && mouse_position_.y != 0))) - { - animation::StartOrReverse(dnd_hide_animation_, animation::Direction::BACKWARD); - drag_edge_touching_ = false; - } - } - - EventLogic(); - auto const& hovered_icon = MouseIconIntersection(mouse_position_.x, mouse_position_.y); - - bool hovered_icon_is_appropriate = false; - if (hovered_icon) - { - if (hovered_icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH) - steal_drag_ = false; - - if (hovered_icon->position() == AbstractLauncherIcon::Position::FLOATING) - hovered_icon_is_appropriate = true; - } - - if (steal_drag_) - { - drag_action_ = nux::DNDACTION_COPY; - if (!dnd_hovered_icon_ && hovered_icon_is_appropriate) - { - dnd_hovered_icon_ = new SpacerLauncherIcon(monitor()); - model_->AddIcon(dnd_hovered_icon_); - model_->ReorderBefore(dnd_hovered_icon_, hovered_icon, true); - } - else if (dnd_hovered_icon_) - { - if (hovered_icon) - { - if (hovered_icon_is_appropriate) - { - model_->ReorderSmart(dnd_hovered_icon_, hovered_icon, true); - } - else - { - dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false, monitor()); - dnd_hovered_icon_->remove.emit(dnd_hovered_icon_); - dnd_hovered_icon_ = nullptr; - } - } - } - } - else - { - if (!drag_edge_touching_ && hovered_icon != dnd_hovered_icon_) - { - if (hovered_icon) - { - hovered_icon->SendDndEnter(); - drag_action_ = hovered_icon->QueryAcceptDrop(dnd_data_); - - if (drag_action_ != nux::DNDACTION_NONE) - hovered_icon->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, true, monitor()); - } - else - { - drag_action_ = nux::DNDACTION_NONE; - } - - if (dnd_hovered_icon_) - { - dnd_hovered_icon_->SendDndLeave(); - dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, false, monitor()); - } - - dnd_hovered_icon_ = hovered_icon; - } - } - - bool accept; - if (drag_action_ != nux::DNDACTION_NONE) - accept = true; - else - accept = false; - - SendDndStatus(accept, drag_action_, nux::Geometry(x, y, 1, 1)); -#endif -} +{ } void Launcher::ProcessDndDrop(int x, int y) -{ -#ifdef USE_X11 - if (steal_drag_) - { - for (auto const& uri : dnd_data_.Uris()) - { - if (DndIsSpecialRequest(uri)) - add_request.emit(uri, dnd_hovered_icon_); - } - } - else if (dnd_hovered_icon_ && drag_action_ != nux::DNDACTION_NONE) - { - if (IsOverlayOpen()) - ubus_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); - - dnd_hovered_icon_->AcceptDrop(dnd_data_); - } - - if (drag_action_ != nux::DNDACTION_NONE) - SendDndFinished(true, drag_action_); - else - SendDndFinished(false, drag_action_); - - // reset our shiz - DndReset(); -#endif -} +{ } /* * Returns the current selected icon if it is in keynavmode @@ -3035,81 +2806,16 @@ int Launcher::GetDragDelta() const } void Launcher::DndStarted(std::string const& data) -{ -#ifdef USE_X11 - SetDndQuirk(); - - dnd_data_.Fill(data.c_str()); - - auto const& uris = dnd_data_.Uris(); - if (std::find_if(uris.begin(), uris.end(), [this] (std::string const& uri) - {return DndIsSpecialRequest(uri);}) != uris.end()) - { - steal_drag_ = true; - - if (IsOverlayOpen()) - SaturateIcons(); - } - else - { - for (auto const& it : *model_) - { - if (it->ShouldHighlightOnDrag(dnd_data_)) - { - it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, false, monitor()); - it->SetQuirk(AbstractLauncherIcon::Quirk::UNFOLDED, true, monitor()); - } - else - { - it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, true, monitor()); - it->SetQuirk(AbstractLauncherIcon::Quirk::UNFOLDED, false, monitor()); - } - } - } -#endif -} +{ } void Launcher::DndFinished() -{ -#ifdef USE_X11 - UnsetDndQuirk(); - - data_checked_ = false; - - DndReset(); -#endif -} +{ } void Launcher::SetDndQuirk() -{ -#ifdef USE_X11 - hide_machine_.SetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE, true); -#endif -} +{ } void Launcher::UnsetDndQuirk() -{ -#ifdef USE_X11 - - if (IsOverlayOpen() && !hovered_) - { - DesaturateIcons(); - } - else - { - for (auto const& it : *model_) - { - it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, false, monitor()); - it->SetQuirk(AbstractLauncherIcon::Quirk::UNFOLDED, false, monitor()); - } - } - - - hide_machine_.SetQuirk(LauncherHideMachine::MT_DRAG_OUT, drag_out_delta_x_ >= DRAG_OUT_PIXELS - 90.0f); - hide_machine_.SetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE, false); - animation::SetValue(dnd_hide_animation_, animation::Direction::BACKWARD); -#endif -} +{ } } // namespace launcher } // namespace unity diff --git a/launcher/StandaloneLauncher.cpp b/launcher/StandaloneLauncher.cpp index 0af7d0c1b..779528357 100644 --- a/launcher/StandaloneLauncher.cpp +++ b/launcher/StandaloneLauncher.cpp @@ -40,7 +40,7 @@ using namespace unity; namespace { -const nux::Size win_size(1024, 768); +const nux::Size win_size(1280, 720); const nux::Color bg_color(95/255.0f, 18/255.0f, 45/255.0f, 1.0f); } diff --git a/launcher/TrashLauncherIcon.cpp b/launcher/TrashLauncherIcon.cpp index 15dc9a611..edf5eea34 100644 --- a/launcher/TrashLauncherIcon.cpp +++ b/launcher/TrashLauncherIcon.cpp @@ -29,7 +29,7 @@ #include "QuicklistMenuItemLabel.h" #include "unity-shared/DesktopApplicationManager.h" -#include "unity-shared/FileManager.h" +#include "unity-shared/GnomeFileManager.h" namespace unity { diff --git a/lockscreen/LockScreenPanel.cpp b/lockscreen/LockScreenPanel.cpp index e54e2f1b4..b3087a5cc 100644 --- a/lockscreen/LockScreenPanel.cpp +++ b/lockscreen/LockScreenPanel.cpp @@ -37,8 +37,8 @@ namespace lockscreen { namespace { -const RawPixel PADDING = 5_em; -const nux::Color BG_COLOR(0.1, 0.1, 0.1, 0.4); +const RawPixel PADDING = 14_em; +nux::Color BG_COLOR(0.1, 0.1, 0.1, 0.9); } using namespace indicator; diff --git a/panel/PanelController.cpp b/panel/PanelController.cpp index f24eb460b..c6496b790 100644 --- a/panel/PanelController.cpp +++ b/panel/PanelController.cpp @@ -66,7 +66,7 @@ Controller::Impl::Impl(Controller* parent, menu::Manager::Ptr const& indicators, : parent_(parent) , indicators_(indicators) , edge_barriers_(edge_barriers) - , opacity_(1.0f) + , opacity_(0.7f) , opacity_maximized_toggle_(false) { UScreen* screen = UScreen::GetDefault(); diff --git a/panel/PanelIndicatorsView.cpp b/panel/PanelIndicatorsView.cpp index 20f06381c..8bf3c8009 100644 --- a/panel/PanelIndicatorsView.cpp +++ b/panel/PanelIndicatorsView.cpp @@ -46,6 +46,7 @@ PanelIndicatorsView::PanelIndicatorsView() { opacity.DisableNotifications(); layout_->SetContentDistribution(nux::MAJOR_POSITION_END); + layout_->SetLeftAndRightPadding(layout_->GetLeftPadding(), 6); SetLayout(layout_); LOG_DEBUG(logger) << "Indicators View Added: "; diff --git a/panel/PanelIndicatorsView.h b/panel/PanelIndicatorsView.h index bb11df48b..8e3a1ec4c 100644 --- a/panel/PanelIndicatorsView.h +++ b/panel/PanelIndicatorsView.h @@ -52,7 +52,7 @@ public: typedef PanelIndicatorEntryView::IndicatorEntryType IndicatorEntryType; PanelIndicatorEntryView* AddEntry(indicator::Entry::Ptr const& entry, - int padding = 5, + int padding = 9, IndicatorEntryPosition pos = AUTO, IndicatorEntryType type = IndicatorEntryType::INDICATOR); void RemoveEntry(indicator::Entry::Ptr const&); diff --git a/panel/PanelMenuView.cpp b/panel/PanelMenuView.cpp index 2bcdd494c..abaa420b4 100644 --- a/panel/PanelMenuView.cpp +++ b/panel/PanelMenuView.cpp @@ -45,8 +45,8 @@ DECLARE_LOGGER(logger, "unity.panel.menu"); namespace { - const RawPixel MAIN_LEFT_PADDING = 4_em; - const RawPixel TITLE_PADDING = 2_em; + const RawPixel MAIN_LEFT_PADDING = 13_em; + const RawPixel TITLE_PADDING = 1_em; const RawPixel MENUBAR_PADDING = 4_em; const int MENU_ENTRIES_PADDING = 6; diff --git a/panel/PanelTray.cpp b/panel/PanelTray.cpp index 20be87f5a..afdf7d4f9 100644 --- a/panel/PanelTray.cpp +++ b/panel/PanelTray.cpp @@ -29,7 +29,7 @@ DECLARE_LOGGER(logger, "unity.panel.tray"); namespace { const std::string SETTINGS_NAME = "com.canonical.Unity.Panel"; -const int PADDING = 3; +const int PADDING = 12; const std::array<std::string, 2> WHITELIST {{ "JavaEmbeddedFrame", "Wine" }}; } @@ -63,7 +63,7 @@ PanelTray::PanelTray(int monitor) GTK_ORIENTATION_HORIZONTAL, (NaTrayFilterCallback)FilterTrayCallback, this); - na_tray_set_icon_size(tray_, panel_height); + na_tray_set_icon_size(tray_, panel_height-6); icon_removed_signal_.Connect(na_tray_get_manager(tray_), "tray_icon_removed", sigc::mem_fun(this, &PanelTray::OnTrayIconRemoved)); @@ -97,7 +97,7 @@ Window PanelTray::xid() void PanelTray::Draw(nux::GraphicsEngine& gfx_context, bool force_draw) { - nux::Geometry const& geo = GetAbsoluteGeometry(); + nux::Geometry geo = GetAbsoluteGeometry(); gfx_context.PushClippingRectangle(geo); nux::GetPainter().PaintBackground(gfx_context, geo); diff --git a/panel/PanelView.cpp b/panel/PanelView.cpp index fd492b62e..1a54ab1a0 100644 --- a/panel/PanelView.cpp +++ b/panel/PanelView.cpp @@ -58,7 +58,7 @@ PanelView::PanelView(MockableBaseWindow* parent, menu::Manager::Ptr const& menus , opacity_maximized_toggle_(false) , needs_geo_sync_(false) , overlay_is_open_(false) - , opacity_(1.0f) + , opacity_(0.7f) , monitor_(0) , stored_dash_width_(0) , bg_effect_helper_(this) @@ -539,7 +539,7 @@ PanelView::UpdateBackground() if (overlay_is_open_ || wm.IsScaleActive()) { - bg_layer_.reset(new nux::ColorLayer(wm.average_color(), true, rop)); + // bg_layer_.reset(new nux::ColorLayer(wm.average_color(), true, rop)); } else { diff --git a/panel/StandalonePanel.cpp b/panel/StandalonePanel.cpp index e13460240..76b4c0e06 100644 --- a/panel/StandalonePanel.cpp +++ b/panel/StandalonePanel.cpp @@ -41,7 +41,7 @@ using namespace unity::panel; struct PanelWindow { PanelWindow() - : wt(nux::CreateGUIThread("Unity Panel", 1024, 24, 0, &PanelWindow::ThreadWidgetInit, this)) + : wt(nux::CreateGUIThread("Unity Panel", 1024, 30, 0, &PanelWindow::ThreadWidgetInit, this)) , animation_controller(tick_source) {} diff --git a/plugins/unityshell/src/unityshell.cpp b/plugins/unityshell/src/unityshell.cpp index ff739f6d6..38f433aaf 100644 --- a/plugins/unityshell/src/unityshell.cpp +++ b/plugins/unityshell/src/unityshell.cpp @@ -3157,73 +3157,9 @@ bool UnityWindow::glDraw(const GLMatrix& matrix, auto draw_panel_shadow = DrawPanelShadow::NO; - if (!(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK)) - { - Window active_window = screen->activeWindow(); - - if (G_UNLIKELY(window_type == CompWindowTypeDesktopMask)) - { - uScreen->setPanelShadowMatrix(matrix); - - if (active_window == 0 || active_window == window->id()) - { - if (PluginAdapter::Default().IsWindowOnTop(window->id())) - { - draw_panel_shadow = DrawPanelShadow::OVER_WINDOW; - } - uScreen->is_desktop_active_ = true; - } - } - else - { - if (window->id() == active_window) - { - draw_panel_shadow = DrawPanelShadow::BELOW_WINDOW; - uScreen->is_desktop_active_ = false; - - if (!(window_state & CompWindowStateMaximizedVertMask) && - !(window_state & CompWindowStateFullscreenMask) && - !(window_type & CompWindowTypeFullscreenMask)) - { - auto const& output = uScreen->screen->currentOutputDev(); - int monitor = uScreen->WM.MonitorGeometryIn(NuxGeometryFromCompRect(output)); - - if (window->y() - window->border().top < output.y() + uScreen->panel_style_.PanelHeight(monitor)) - { - draw_panel_shadow = DrawPanelShadow::OVER_WINDOW; - } - } - } - else if (uScreen->menus_->integrated_menus()) - { - draw_panel_shadow = DrawPanelShadow::BELOW_WINDOW; - } - else - { - if (uScreen->is_desktop_active_) - { - if (PluginAdapter::Default().IsWindowOnTop(window->id())) - { - draw_panel_shadow = DrawPanelShadow::OVER_WINDOW; - uScreen->panelShadowPainted = CompRegion(); - } - } - } - } - } - - if (locked) - draw_panel_shadow = DrawPanelShadow::NO; - - if (draw_panel_shadow == DrawPanelShadow::BELOW_WINDOW) - uScreen->paintPanelShadow(region); - deco_win_->Draw(matrix, attrib, region, mask); bool ret = gWindow->glDraw(matrix, attrib, region, mask); - if (draw_panel_shadow == DrawPanelShadow::OVER_WINDOW) - uScreen->paintPanelShadow(region); - return ret; } diff --git a/plugins/unityshell/unityshell.xml.in b/plugins/unityshell/unityshell.xml.in index 9294c5b26..61afdeac4 100644 --- a/plugins/unityshell/unityshell.xml.in +++ b/plugins/unityshell/unityshell.xml.in @@ -104,8 +104,8 @@ <option name="panel_opacity" type="float"> <_short>Panel Opacity</_short> <_long>The opacity of the Panel background.</_long> - <default>1.0</default> - <min>0.0</min> + <default>0.75</default> + <min>0.1</min> <max>1.0</max> <precision>0.01</precision> </option> @@ -419,7 +419,7 @@ <option name="icon_size" type="int"> <_short>Launcher Icon Size</_short> <_long>The size of the icons in the Launcher.</_long> - <default>48</default> + <default>44</default> <min>8</min> <max>64</max> <precision>1</precision> @@ -430,7 +430,7 @@ <_long>Change how the icons in the Launcher are backlit.</_long> <min>0</min> <max>4</max> - <default>1</default> + <default>4</default> <desc> <value>0</value> <_name>Backlight Always On</_name> diff --git a/resources/dash_sheen.png b/resources/dash_sheen.png Binary files differindex fb31cdb6e..7cceadb76 100644 --- a/resources/dash_sheen.png +++ b/resources/dash_sheen.png diff --git a/resources/empty.png b/resources/empty.png Binary files differnew file mode 100644 index 000000000..7cceadb76 --- /dev/null +++ b/resources/empty.png diff --git a/resources/launcher_bfb.svg b/resources/launcher_bfb.svg index 74e60e9c8..b6f1ccb0a 100644 --- a/resources/launcher_bfb.svg +++ b/resources/launcher_bfb.svg @@ -1,13 +1,126 @@ -<svg version="1.1" viewBox="0 0 136.53 136.53" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <defs> - <filter id="a" x="-.075" y="-.075" width="1.15" height="1.15" color-interpolation-filters="sRGB"> - <feGaussianBlur stdDeviation="2.4999998"/> - </filter> - </defs> - <image width="136.53" height="136.53" preserveAspectRatio="none" xlink:href="data:image/png;base64,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 4K+6lV/77/8DTX3gKAGJXUUAAAAASUVORK5CYII= "/> - <g> - <ellipse cx="68.267" cy="68.267" rx="40" ry="40" fill="#9005d5"/> - <path d="m68.244 28.267a40 40 0 0 0-39.978 40 40 40 0 0 0 40 40 40 40 0 0 0 40-40 40 40 0 0 0-40-40 40 40 0 0 0-0.0223 0zm0.43912 12.8a27.2 27.2 0 0 1 5.2476 0.51106l-3.8886 6.7353a20 20 0 0 0-1.359-0.04632 20 20 0 0 0-1.3588 0.04608l-3.4e-4 -5.52e-4a20 20 0 0 0-8.6411 2.6333 20 20 0 0 0-2e-3 8e-4 20 20 0 0 0-6.5993 6.1657l4.9e-4 5.44e-4a20 20 0 0 0-1.359 2.3538l-7.7772 9.6e-5a27.2 27.2 0 0 1 5.2474-9.0889l-5.5e-4 -5.52e-4a27.2 27.2 0 0 1 5.4498-4.7744l1.9133 3.314 2.933-1.6933-1.9133-3.3139a27.2 27.2 0 0 1 6.8594-2.332l4.4e-4 5.44e-4a27.2 27.2 0 0 1 5.2474-0.51097zm8.6027 1.4841 5.7186 9.6e-5c0.38588 7.2e-5 0.73999 0.20394 0.93828 0.5309l2.8616 4.7698c0.0837 0.12732 0.17604 0.33828 0.16272 0.60168-8e-3 0.15803-0.0538 0.33549-0.16272 0.52421l-2.8616 4.7756c-0.19839 0.32682-0.55229 0.53025-0.9382 0.53024l-5.7186-8e-5c-0.19298 1.28e-4 -0.37827-0.04912-0.53974-0.14023-0.16143-0.09116-0.29882-0.22444-0.39836-0.39034l-2.862-4.7755c-0.20902-0.34837-0.20905-0.77678 0-1.1251l2.862-4.7706c0.0992-0.16344 0.23645-0.29694 0.39854-0.38769 0.16151-0.09178 0.34654-0.14284 0.53949-0.14288zm-11.115 3.7637c0.0455 0.08294 0.0932 0.16574 0.14346 0.24846zm23.002 4.0632v1.76e-4a27.2 27.2 0 0 1 5.2475 9.0889h-5e-3a27.2 27.2 0 0 1 1.41 7.1068h-3.8789v3.3867h3.8793a27.2 27.2 0 0 1-1.4101 7.1067h4e-3a27.2 27.2 0 0 1-5.2475 9.089l-3.8886-6.7352a20 20 0 0 0 1.359-2.3536h-4e-3a20 20 0 0 0 2.0401-8.8 20 20 0 0 0-5.6e-4 -0.0024 20 20 0 0 0-2.0399-8.7985l5e-3 8e-5a20 20 0 0 0-1.3584-2.3536zm-46.255 11.84 5.5666 0.09045c0.38224 0.0084 0.73533 0.21324 0.92828 0.54744l2.8592 4.9525c0.0966 0.16707 0.14668 0.3521 0.1485 0.53751l-9e-5 -8e-5c2e-3 0.18537-0.045 0.37099-0.1389 0.54017l-2.7046 4.8664c-0.19718 0.35519-0.5682 0.56943-0.9744 0.56257l-5.5624-0.09335c-0.19113-4e-3 -0.3754-0.05632-0.53504-0.1513-0.16024-0.09399-0.29698-0.22863-0.39349-0.39572l-2.8592-4.9525c-0.19287-0.33421-0.19334-0.7428-9e-3 -1.078l2.6999-4.8631c0.0684-0.13618 0.20503-0.3216 0.43979-0.44177 0.14087-0.07208 0.31738-0.12122 0.53528-0.12122zm0.0284 14.849h7.7773a20 20 0 0 0 1.3589 2.3536l-6e-3 0.0097a20 20 0 0 0 15.242 8.8l-3.883 6.7258 3.8886-6.7352a20 20 0 0 0 1.3588 0.04608 20 20 0 0 0 1.3591-0.04664l3.8885 6.7354a27.2 27.2 0 0 1-5.2475 0.51106 27.2 27.2 0 0 1-5.2476-0.51106l-6e-3 9e-3a27.2 27.2 0 0 1-6.8595-2.332l1.9133-3.314-2.933-1.6934-1.9134 3.314a27.2 27.2 0 0 1-5.4496-4.7746l6e-3 -0.0094a27.2 27.2 0 0 1-5.2475-9.089zm40.045 5.0986c0.40294-0.0064 0.77107 0.20574 0.96664 0.55811l2.6786 4.8249c0.0913 0.16622 0.13786 0.35033 0.13544 0.53461-8e-4 0.18428-0.0492 0.36861-0.14487 0.53439l-2.8364 4.9126c-0.19146 0.33149-0.5422 0.5345-0.92142 0.54266l-5.5169 0.0926c-0.1509 9e-3 -0.37795-0.0166-0.59762-0.15867-0.13179-0.0853-0.26151-0.2125-0.36958-0.39967l-2.6833-4.827c-0.18236-0.33255-0.18149-0.7375 0.01-1.069l2.8364-4.9127c0.0956-0.16586 0.22973-0.30062 0.38811-0.39414 0.15837-0.09346 0.34107-0.14544 0.53297-0.14866z" filter="url(#a)" opacity=".5" style="mix-blend-mode:normal;paint-order:stroke markers fill"/> - <path d="m68.244 28.267a40 40 0 0 0-39.978 40 40 40 0 0 0 40 40 40 40 0 0 0 40-40 40 40 0 0 0-40-40 40 40 0 0 0-0.0223 0zm0.43913 12.8a27.2 27.2 0 0 1 5.2476 0.51106l-3.8886 6.7353a20 20 0 0 0-1.359-0.04632 20 20 0 0 0-1.3588 0.04608l-3.4e-4 -5.52e-4a20 20 0 0 0-8.6411 2.6333 20 20 0 0 0-2e-3 8e-4 20 20 0 0 0-6.5993 6.1657l4.9e-4 5.44e-4a20 20 0 0 0-1.359 2.3538l-7.7773 9.6e-5a27.2 27.2 0 0 1 5.2474-9.0889l-5.5e-4 -5.52e-4a27.2 27.2 0 0 1 5.4498-4.7744l1.9133 3.314 2.933-1.6933-1.9133-3.3139a27.2 27.2 0 0 1 6.8594-2.332l4.3e-4 5.44e-4a27.2 27.2 0 0 1 5.2474-0.51097zm8.6027 1.4841 5.7186 9.6e-5c0.38587 7.2e-5 0.73998 0.20394 0.93827 0.5309l2.8616 4.7698c0.0837 0.12732 0.17603 0.33828 0.16272 0.60168-8e-3 0.15803-0.0538 0.33549-0.16272 0.52421l-2.8616 4.7756c-0.19839 0.32682-0.55229 0.53025-0.9382 0.53024l-5.7186-8e-5c-0.19299 1.28e-4 -0.37827-0.04912-0.53975-0.14023-0.16143-0.09116-0.29881-0.22444-0.39836-0.39034l-2.862-4.7755c-0.20903-0.34837-0.20906-0.77678 0-1.1251l2.862-4.7706c0.0992-0.16344 0.23644-0.29694 0.39853-0.38769 0.16151-0.09178 0.34655-0.14284 0.5395-0.14288zm-11.115 3.7637c0.0455 0.08294 0.0932 0.16574 0.14346 0.24846zm23.002 4.0632v1.76e-4a27.2 27.2 0 0 1 5.2475 9.0889h-5e-3a27.2 27.2 0 0 1 1.41 7.1068h-3.8789v3.3867h3.8793a27.2 27.2 0 0 1-1.4102 7.1067h4e-3a27.2 27.2 0 0 1-5.2475 9.089l-3.8886-6.7352a20 20 0 0 0 1.359-2.3536h-4e-3a20 20 0 0 0 2.0401-8.8 20 20 0 0 0-5.5e-4 -0.0024 20 20 0 0 0-2.0399-8.7985l5e-3 8e-5a20 20 0 0 0-1.3584-2.3536zm-46.255 11.84 5.5666 0.09045c0.38223 0.0084 0.73532 0.21324 0.92828 0.54744l2.8592 4.9525c0.0966 0.16707 0.14668 0.3521 0.1485 0.53751l-9e-5 -8e-5c2e-3 0.18537-0.045 0.37099-0.1389 0.54017l-2.7046 4.8664c-0.19719 0.35519-0.5682 0.56943-0.97441 0.56257l-5.5624-0.09335c-0.19113-4e-3 -0.3754-0.05632-0.53504-0.1513-0.16024-0.09399-0.29697-0.22863-0.39349-0.39572l-2.8592-4.9525c-0.19287-0.33421-0.19333-0.7428-9e-3 -1.078l2.6999-4.8631c0.0684-0.13618 0.20502-0.3216 0.43978-0.44177 0.14087-0.07208 0.31738-0.12122 0.53529-0.12122zm0.0284 14.849h7.7773a20 20 0 0 0 1.3589 2.3536l-6e-3 0.0097a20 20 0 0 0 15.242 8.8l-3.883 6.7258 3.8886-6.7352a20 20 0 0 0 1.3588 0.0461 20 20 0 0 0 1.3591-0.0466l3.8885 6.7354a27.2 27.2 0 0 1-5.2475 0.51106 27.2 27.2 0 0 1-5.2476-0.51106l-6e-3 9e-3a27.2 27.2 0 0 1-6.8595-2.332l1.9133-3.314-2.933-1.6934-1.9134 3.3141a27.2 27.2 0 0 1-5.4496-4.7746l6e-3 -9e-3a27.2 27.2 0 0 1-5.2475-9.0889zm40.045 5.0986c0.40295-0.0064 0.77107 0.20574 0.96664 0.55811l2.6786 4.8249c0.0913 0.16621 0.13786 0.35032 0.13544 0.5346-8e-4 0.18428-0.0492 0.36861-0.14486 0.5344l-2.8364 4.9126c-0.19146 0.3315-0.54219 0.53451-0.92142 0.54267l-5.5169 0.0926c-0.15091 9e-3 -0.37795-0.0166-0.59763-0.15868-0.13179-0.0853-0.26151-0.21249-0.36958-0.39967l-2.6833-4.827c-0.18236-0.33255-0.18148-0.73749 0.01-1.069l2.8364-4.9127c0.0956-0.16586 0.22974-0.30062 0.38811-0.39414 0.15838-0.09346 0.34107-0.14544 0.53297-0.14866z" fill="#fff" style="paint-order:stroke markers fill"/> - </g> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + version="1.1" + id="svg2" + width="136.53333" + height="136.53333" + viewBox="0 0 136.53333 136.53333" + sodipodi:docname="launcher_bfb.svg" + inkscape:export-filename="/home/muqtadir/Desktop/unity-orb1.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + <metadata + id="metadata8"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs6"> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient927" + id="linearGradient1686-0" + x1="321.99991" + y1="84.000153" + x2="365.99991" + y2="84.000153" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0,2.9046321,-2.9677501,0,317.55806,-932.37102)" /> + <linearGradient + inkscape:collect="always" + id="linearGradient927"> + <stop + style="stop-color:#f3e6fa;stop-opacity:1" + offset="0" + id="stop923" /> + <stop + id="stop933" + offset="0.125" + style="stop-color:#f3e6fa;stop-opacity:0.09803922" /> + <stop + id="stop931" + offset="0.92500001" + style="stop-color:#f3e6fa;stop-opacity:0.09803922" /> + <stop + style="stop-color:#f3e6fa;stop-opacity:0.49803922" + offset="1" + id="stop925" /> + </linearGradient> + </defs> + <sodipodi:namedview + pagecolor="#3d3d3d" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1920" + inkscape:window-height="965" + id="namedview4" + showgrid="false" + units="px" + inkscape:zoom="2.5" + inkscape:cx="80.2" + inkscape:cy="76.6" + inkscape:window-x="0" + inkscape:window-y="36" + inkscape:window-maximized="1" + inkscape:current-layer="svg2" + inkscape:document-rotation="0" + inkscape:pagecheckerboard="0" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.87204361;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 37.573433,2.9202406 c -7.781739,0 -13.909103,0.1455247 -18.950896,0.8328467 C 13.580716,4.4404388 9.4550382,5.7297171 6.4329484,8.1783524 3.4108883,10.626984 1.8158115,13.976199 0.97739229,18.072688 0.13894348,22.169208 -0.02472795,27.156491 0.01340764,33.491722 v 36.234721 36.252077 c -0.03769042,6.32548 0.12642616,11.31024 0.96398465,15.40234 0.83841921,4.09652 2.43349601,7.44572 5.45555611,9.89435 3.0220898,2.44864 7.1477676,3.73791 12.1895886,4.42524 5.041852,0.68739 11.169157,0.83288 18.950896,0.83288 h 61.391416 c 7.781821,0 13.905931,-0.14514 18.937331,-0.83288 5.03135,-0.68762 9.14441,-1.98125 12.15534,-4.4308 3.01099,-2.44948 4.5966,-5.79631 5.44188,-9.88879 0.8453,-4.09242 1.02547,-9.06961 1.02547,-15.40234 V 69.726443 33.475042 c 0,-6.332726 -0.18044,-11.309873 -1.02547,-15.402354 -0.84528,-4.092451 -2.43092,-7.439254 -5.44191,-9.8887903 C 127.04657,5.7343641 122.9335,4.4407284 117.90212,3.7530873 112.87078,3.0654433 106.74662,2.9202406 98.964849,2.9202406 Z" + id="path1020" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sccsccccccsscccscsccsss" /> + <path + style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.93602157;marker:none;enable-background:accumulate;opacity:0.05" + d="m 36.616066,1.46807 c -29.710622,0 -35.2766675,2.6326143 -35.1205639,28.315452 v 37.038633 37.039335 c -0.1564004,25.68283 5.4099419,28.31544 35.1205639,28.31544 h 63.304487 c 29.703207,0 35.120497,-2.63232 35.120497,-28.31544 V 66.822155 29.783522 c 0,-25.6831304 -5.41729,-28.315452 -35.120527,-28.315452 z" + id="path964" + inkscape:connector-curvature="0" + sodipodi:nodetypes="scccssscsss" /> + <path + style="display:inline;opacity:0.2;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:5.87204313;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;enable-background:new" + d="M 122.82763,7.1410191 4.1773923,118.58086 v 0 c 0,0 2.7330968,13.59552 32.4363287,13.59552 h 63.30235 c 29.710619,0 35.276429,-2.63162 35.120329,-28.31451 V 66.822155 29.78238 C 133.55543,5.2605566 129.804,6.4966209 122.82763,7.1410191 Z" + id="path962" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccssscccc" /> + <path + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.4;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.93602157;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" + d="m 36.616066,1.4680407 c -29.710622,0 -35.2766675,2.6326143 -35.1205639,28.3154523 v 37.038662 37.039335 c -0.1564004,25.68283 5.4099419,28.31544 35.1205639,28.31544 h 63.304487 c 29.703207,0 35.120497,-2.63232 35.120497,-28.31544 V 66.822155 29.783493 c 0,-25.6831306 -5.41729,-28.3154523 -35.120527,-28.3154523 z" + id="path958" + inkscape:connector-curvature="0" + sodipodi:nodetypes="scccssscsss" /> + <path + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.6;fill:url(#linearGradient1686-0);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.93602157;marker:none;enable-background:accumulate" + d="m 36.618351,2.9202406 c -14.810527,0 -23.357707,0.8024923 -27.8516528,4.2207785 C 6.5197254,8.8501622 5.0937808,11.21773 4.1817615,14.867805 3.2697718,18.517883 2.9433191,23.40751 2.9819001,29.776733 v 37.045422 37.045395 c -0.038878,6.36922 0.2878717,11.25888 1.1998614,14.90894 0.9120193,3.65006 2.3379639,6.0176 4.5849367,7.72679 4.4939748,3.41826 13.0411258,4.2208 27.8516528,4.2208 h 63.302351 c 14.806778,0 23.311728,-0.80368 27.787868,-4.2208 2.23804,-1.70861 3.65904,-4.07987 4.57915,-7.73244 0.9201,-3.65257 1.2694,-8.53814 1.2694,-14.90897 V 66.822155 29.78238 c 0,-6.370773 -0.3493,-11.256335 -1.2694,-14.90891 -0.92011,-3.652576 -2.34108,-6.0238287 -4.57915,-7.7324509 C 123.23243,3.7238069 114.72752,2.9202406 99.920702,2.9202406 Z m 7.129545,3.1882707 c 20.618059,0.00241 41.283276,0.00878 61.876434,0.1588686 6.35977,0.2408047 13.24896,-0.4290153 19.06432,2.70607 3.84648,2.0193891 4.59592,6.5363811 5.25152,10.3534451 0.52087,6.398586 0.27156,12.903067 0.41154,19.37366 0.0674,24.629916 0.22852,49.288685 -0.23771,73.914945 -0.47932,4.30826 -1.13175,9.6021 -5.33845,12.03266 -4.76473,2.4071 -10.31445,2.63285 -15.5749,2.76847 -28.192857,0.19695 -56.459373,0.24494 -84.650447,-0.0512 -4.683585,-0.3315 -9.706116,-0.69753 -13.749023,-3.25637 C 7.1522717,121.21787 6.7626061,116.18701 6.3379507,111.92893 5.9604529,85.791447 6.0898469,59.632601 6.1815503,33.481246 6.477435,26.564529 5.4533237,19.248544 8.2740517,12.68364 10.267341,8.2984563 15.591723,7.2811389 19.965355,6.709856 27.864557,5.790309 35.818602,6.282816 43.747926,6.1085113 Z" + id="path964-3" + inkscape:connector-curvature="0" /> + <path + id="path12" + style="fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:0.0633053;stroke-linejoin:round;paint-order:stroke markers fill" + d="M 68.386946,28.008705 A 46.000001,45.999994 0 0 0 22.412573,74.008748 46.000001,45.999994 0 0 0 68.412617,120.0087 46.000001,45.999994 0 0 0 114.41258,74.008748 46.000001,45.999994 0 0 0 68.412617,28.008705 a 46.000001,45.999994 0 0 0 -0.02568,0 z m 0.504997,14.719959 a 31.280001,31.279996 0 0 1 6.034734,0.587714 l -4.471922,7.745593 a 22.999998,22.999995 0 0 0 -1.562812,-0.05322 22.999998,22.999995 0 0 0 -1.562626,0.05296 l -3.9e-4,-6.39e-4 a 22.999996,22.999993 0 0 0 -9.93729,3.028352 22.999996,22.999993 0 0 0 -0.0019,0.0013 22.999996,22.999993 0 0 0 -7.589182,7.090553 l 5.69e-4,6.39e-4 a 22.999998,22.999995 0 0 0 -1.562812,2.70687 l -8.94385,1.02e-4 a 31.280001,31.279996 0 0 1 6.034544,-10.452199 l -6.39e-4,-6.39e-4 a 31.279999,31.279994 0 0 1 6.267219,-5.490949 l 2.200263,3.811087 3.372937,-1.947333 -2.200263,-3.810991 a 31.279999,31.279994 0 0 1 7.888316,-2.681863 l 4.92e-4,6.39e-4 a 31.280001,31.279996 0 0 1 6.034542,-0.587624 z m 9.893081,1.70667 6.576339,1.02e-4 c 0.443759,9.6e-5 0.85098,0.234549 1.079019,0.610535 l 3.290878,5.485244 c 0.09631,0.146414 0.202439,0.389025 0.187131,0.691929 -0.0092,0.181745 -0.06185,0.385818 -0.187131,0.602836 l -3.290878,5.491901 c -0.228147,0.375839 -0.635132,0.609781 -1.07893,0.609774 l -6.576428,-9.5e-5 c -0.221931,1.47e-4 -0.435013,-0.05648 -0.620713,-0.161262 v 0 c -0.185648,-0.104836 -0.343634,-0.258105 -0.458116,-0.448896 l -3.291262,-5.491805 c -0.240375,-0.400615 -0.240407,-0.893301 0,-1.29391 l 3.291351,-5.486195 c 0.114023,-0.187967 0.271913,-0.341486 0.458315,-0.445848 0.185738,-0.105545 0.398526,-0.164265 0.620425,-0.16431 z m -12.782417,4.328254 c 0.05232,0.09538 0.10718,0.190606 0.164967,0.28573 z m 26.452543,4.672647 v 1.98e-4 a 31.280001,31.279996 0 0 1 6.034644,10.452272 h -0.0051 a 31.279997,31.279992 0 0 1 1.621486,8.172806 h -4.460725 v 3.894666 h 4.461185 a 31.279997,31.279992 0 0 1 -1.621669,8.172716 h 0.0045 a 31.280001,31.279996 0 0 1 -6.034666,10.452354 l -4.471928,-7.745485 a 22.999998,22.999995 0 0 0 1.562812,-2.706582 h -0.0045 a 22.999995,22.999991 0 0 0 2.346115,-10.119953 22.999995,22.999991 0 0 0 -6.38e-4,-0.0026 22.999995,22.999991 0 0 0 -2.345917,-10.118247 l 0.0051,1.02e-4 a 22.999998,22.999995 0 0 0 -1.562167,-2.706684 z m -53.193199,13.616503 6.401571,0.104017 c 0.439568,0.0096 0.845627,0.245225 1.067519,0.629555 l 3.288118,5.69538 c 0.11109,0.192133 0.16868,0.404928 0.170775,0.618144 l -1e-4,-9.6e-5 c 0.0019,0.213185 -0.05175,0.42665 -0.159741,0.621192 l -3.110315,5.596397 c -0.226761,0.408474 -0.653437,0.654848 -1.120573,0.646958 l -6.396818,-0.107352 c -0.219797,-0.0045 -0.43171,-0.06476 -0.615294,-0.174008 -0.184275,-0.108081 -0.341525,-0.262928 -0.452506,-0.455074 L 35.046462,74.53247 c -0.221803,-0.384342 -0.222333,-0.854226 -0.01073,-1.239712 l 3.104891,-5.59259 c 0.07864,-0.15661 0.235782,-0.36984 0.505757,-0.508038 0.161997,-0.0829 0.364985,-0.139399 0.615576,-0.139392 z m 0.03265,17.075772 h 8.94385 a 22.999998,22.999995 0 0 0 1.562716,2.706677 l -0.0065,0.01112 a 22.999998,22.999995 0 0 0 17.528377,10.119981 l -4.465457,7.734642 4.471922,-7.745439 a 22.999998,22.999995 0 0 0 1.56262,0.05303 22.999998,22.999995 0 0 0 1.563004,-0.05367 l 4.471736,7.745699 a 31.280001,31.279996 0 0 1 -6.034644,0.58771 31.280001,31.279996 0 0 1 -6.034733,-0.58771 l -0.0064,0.0109 a 31.279999,31.279994 0 0 1 -7.888357,-2.68187 l 2.200352,-3.811096 -3.372937,-1.947399 -2.200353,3.811165 a 31.279999,31.279994 0 0 1 -6.267033,-5.490739 l 0.0065,-0.01086 a 31.280001,31.279996 0 0 1 -6.034638,-10.45228 z m 46.05177,5.863389 c 0.463386,-0.0078 0.886733,0.236599 1.111641,0.641821 l 3.080454,5.548583 c 0.104918,0.191155 0.15854,0.402883 0.155748,0.614804 -0.0013,0.21192 -0.05654,0.423903 -0.16659,0.614547 l -3.261873,5.649506 c -0.22018,0.38122 -0.623523,0.61467 -1.059629,0.62406 l -6.344429,0.10651 c -0.173541,0.0102 -0.434642,-0.0192 -0.687271,-0.18247 -0.151558,-0.0981 -0.300738,-0.24438 -0.425023,-0.45962 l -3.085788,-5.551047 c -0.209715,-0.382439 -0.208706,-0.848127 0.01144,-1.229351 l 3.26188,-5.649566 c 0.109953,-0.190734 0.264194,-0.345709 0.446328,-0.453266 v 0 c 0.182135,-0.107487 0.39224,-0.167248 0.612918,-0.17096 z" /> + <path + id="path12-3" + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.0633053;stroke-linejoin:round;paint-order:stroke markers fill" + d="M 68.38737,22.009002 A 46.000001,45.999994 0 0 0 22.413,68.009045 46.000001,45.999994 0 0 0 68.41304,114.009 46.000001,45.999994 0 0 0 114.413,68.009045 46.000001,45.999994 0 0 0 68.41304,22.009002 a 46.000001,45.999994 0 0 0 -0.0257,0 z m 0.505,14.719959 a 31.280001,31.279996 0 0 1 6.03473,0.587714 l -4.47192,7.745593 a 22.999998,22.999995 0 0 0 -1.56281,-0.05322 22.999998,22.999995 0 0 0 -1.56263,0.05296 l -3.9e-4,-6.39e-4 a 22.999996,22.999993 0 0 0 -9.93729,3.028352 22.999996,22.999993 0 0 0 -0.002,0.0013 22.999996,22.999993 0 0 0 -7.58918,7.090553 l 5.7e-4,6.39e-4 a 22.999998,22.999995 0 0 0 -1.56281,2.70687 l -8.94385,1.02e-4 a 31.280001,31.279996 0 0 1 6.03454,-10.452199 l -6.4e-4,-6.39e-4 a 31.279999,31.279994 0 0 1 6.26722,-5.490949 l 2.20026,3.811087 3.37294,-1.947333 -2.20026,-3.810991 a 31.279999,31.279994 0 0 1 7.88831,-2.681863 l 5e-4,6.39e-4 a 31.280001,31.279996 0 0 1 6.03454,-0.587624 z m 9.89308,1.70667 6.57634,1.02e-4 c 0.44376,9.6e-5 0.85098,0.234549 1.07902,0.610535 l 3.29087,5.485244 c 0.0963,0.146414 0.20244,0.389025 0.18714,0.691929 -0.009,0.181745 -0.0618,0.385818 -0.18714,0.602836 l -3.29087,5.491901 c -0.22815,0.375839 -0.63513,0.609781 -1.07893,0.609774 l -6.57643,-9.5e-5 c -0.22193,1.47e-4 -0.43501,-0.05648 -0.62071,-0.161262 v 0 C 77.97909,51.661759 77.8211,51.50849 77.70662,51.317699 l -3.29126,-5.491805 c -0.24038,-0.400615 -0.24041,-0.893301 0,-1.29391 l 3.29135,-5.486195 c 0.11402,-0.187967 0.27191,-0.341486 0.45831,-0.445848 0.18574,-0.105545 0.39853,-0.164265 0.62043,-0.16431 z m -12.78242,4.328254 c 0.0523,0.09538 0.10718,0.190606 0.16497,0.28573 z m 26.45255,4.672647 v 1.98e-4 a 31.280001,31.279996 0 0 1 6.03464,10.452272 h -0.005 a 31.279997,31.279992 0 0 1 1.62148,8.172806 h -4.46072 v 3.894666 h 4.46118 A 31.279997,31.279992 0 0 1 98.4855,78.12919 h 0.004 A 31.280001,31.279996 0 0 1 92.45483,88.581544 L 87.9829,80.836059 a 22.999998,22.999995 0 0 0 1.56281,-2.706582 h -0.005 a 22.999995,22.999991 0 0 0 2.34612,-10.119953 22.999995,22.999991 0 0 0 -6.4e-4,-0.0026 22.999995,22.999991 0 0 0 -2.34592,-10.118247 l 0.005,1.02e-4 a 22.999998,22.999995 0 0 0 -1.56216,-2.706684 z m -53.1932,13.616503 6.40157,0.104017 c 0.43956,0.0096 0.84562,0.245225 1.06752,0.629555 l 3.28811,5.69538 c 0.11109,0.192133 0.16868,0.404928 0.17078,0.618144 l -10e-5,-9.6e-5 c 0.002,0.213185 -0.0517,0.42665 -0.15974,0.621192 l -3.11032,5.596397 c -0.22676,0.408474 -0.65343,0.654848 -1.12057,0.646958 L 39.40281,74.85723 c -0.2198,-0.0045 -0.43171,-0.06476 -0.61529,-0.174008 -0.18428,-0.108081 -0.34153,-0.262928 -0.45251,-0.455074 l -3.28812,-5.695381 c -0.22181,-0.384342 -0.22234,-0.854226 -0.0107,-1.239712 l 3.10489,-5.59259 c 0.0786,-0.15661 0.23578,-0.36984 0.50575,-0.508038 0.162,-0.0829 0.36499,-0.139399 0.61558,-0.139392 z m 0.0327,17.075772 h 8.94385 a 22.999998,22.999995 0 0 0 1.56271,2.706677 l -0.006,0.01112 A 22.999998,22.999995 0 0 0 67.32402,90.96658 l -4.46546,7.73465 4.47192,-7.74544 a 22.999998,22.999995 0 0 0 1.56262,0.053 22.999998,22.999995 0 0 0 1.56301,-0.0537 l 4.47173,7.74569 A 31.280001,31.279996 0 0 1 68.8932,99.2885 31.280001,31.279996 0 0 1 62.85847,98.70078 l -0.006,0.0109 a 31.279999,31.279994 0 0 1 -7.88836,-2.68186 l 2.20035,-3.8111 -3.37294,-1.9474 -2.20035,3.81117 a 31.279999,31.279994 0 0 1 -6.26703,-5.490742 l 0.007,-0.01086 A 31.280001,31.279996 0 0 1 39.2965,78.128668 Z m 46.05177,5.863389 c 0.46338,-0.0078 0.88673,0.236599 1.11164,0.641821 l 3.08045,5.548583 c 0.10492,0.19115 0.15854,0.40288 0.15575,0.6148 -10e-4,0.21192 -0.0565,0.42391 -0.16659,0.61455 l -3.26187,5.6495 c -0.22018,0.38123 -0.62353,0.61468 -1.05963,0.62407 l -6.34443,0.1065 c -0.17354,0.0102 -0.43464,-0.0192 -0.68727,-0.18246 -0.15156,-0.0981 -0.30074,-0.24438 -0.42503,-0.45962 l -3.08578,-5.55105 c -0.20972,-0.38244 -0.20871,-0.84813 0.0114,-1.22935 l 3.26188,-5.649567 c 0.10995,-0.190734 0.26419,-0.345709 0.44632,-0.453266 v 0 c 0.18214,-0.107487 0.39224,-0.167248 0.61292,-0.17096 z" /> </svg> diff --git a/resources/panel_shadow.png b/resources/panel_shadow.png Binary files differindex d76818a65..7cceadb76 100644 --- a/resources/panel_shadow.png +++ b/resources/panel_shadow.png diff --git a/resources/refine_gradient_dash.png b/resources/refine_gradient_dash.png Binary files differindex 17214cc5f..7cceadb76 100644 --- a/resources/refine_gradient_dash.png +++ b/resources/refine_gradient_dash.png diff --git a/resources/refine_gradient_panel.png b/resources/refine_gradient_panel.png Binary files differindex 0b58160ae..7cceadb76 100644 --- a/resources/refine_gradient_panel.png +++ b/resources/refine_gradient_panel.png diff --git a/resources/refine_gradient_panel_single_column.png b/resources/refine_gradient_panel_single_column.png Binary files differindex 38b9b4956..7cceadb76 100644 --- a/resources/refine_gradient_panel_single_column.png +++ b/resources/refine_gradient_panel_single_column.png diff --git a/shutdown/SessionButton.cpp b/shutdown/SessionButton.cpp index 61a6e8e11..6762c888e 100644 --- a/shutdown/SessionButton.cpp +++ b/shutdown/SessionButton.cpp @@ -132,11 +132,11 @@ void Button::UpdateTextures(std::string const& texture_prefix) { auto const& theme = theme::Settings::Get(); auto texture_path = theme->ThemedFilePath(texture_prefix, {PKGDATADIR}); - RawPixel const texture_size = GetDefaultMaxTextureSize(texture_path); + RawPixel const texture_size = GetDefaultMaxTextureSize(texture_path) * 0.8; normal_tex_.Adopt(nux::CreateTexture2DFromFile(texture_path.c_str(), texture_size.CP(scale), true)); auto texture_highlight_path = theme->ThemedFilePath(texture_prefix + "_highlight", {PKGDATADIR}); - RawPixel const texture_highlight_size = GetDefaultMaxTextureSize(texture_path); + RawPixel const texture_highlight_size = GetDefaultMaxTextureSize(texture_path) * 0.8; highlight_tex_.Adopt(nux::CreateTexture2DFromFile(texture_highlight_path.c_str(), texture_highlight_size.CP(scale), true)); } diff --git a/shutdown/SessionView.cpp b/shutdown/SessionView.cpp index 422e3c934..a5667c2c2 100644 --- a/shutdown/SessionView.cpp +++ b/shutdown/SessionView.cpp @@ -39,7 +39,7 @@ namespace style RawPixel const LEFT_RIGHT_PADDING = 30_em; RawPixel const TOP_PADDING = 19_em; RawPixel const BOTTOM_PADDING = 12_em; - RawPixel const MAIN_SPACE = 10_em; + RawPixel const MAIN_SPACE = 17_em; RawPixel const BUTTONS_SPACE = 20_em; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 58a8b1089..82d92aaaf 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -281,7 +281,6 @@ if (GMOCK_LIB AND add_unity_test_xless (launcher-options) add_unity_test_xless (layout-system) add_unity_test_xless (model-iterator) - add_unity_test_xless (previews) add_unity_test_xless (raw-pixel) add_unity_test_xless (scope-data) add_unity_test_xless (time-util) @@ -382,7 +381,6 @@ if (ENABLE_X_SUPPORT) add_unity_test (dash-controller) add_unity_test (desktop-launcher-icon) add_unity_test (device-launcher-section) - add_unity_test (error-preview) add_unity_test (edge-barrier-controller) add_unity_test (expo-launcher-icon) add_unity_test (file-manager-launcher-icon) @@ -418,14 +416,6 @@ if (ENABLE_X_SUPPORT) add_unity_test (panel-tray) add_unity_test (panel-view) add_unity_test (places-group) - add_unity_test (preview-player) - add_unity_test (previews-application) - add_unity_test (previews-generic) - add_unity_test (previews-movie) - add_unity_test (previews-music) - add_unity_test (previews-music-payment) - add_unity_test (previews-payment) - add_unity_test (previews-social) add_unity_test (quicklist-manager) add_unity_test (quicklist-menu-item) add_unity_test (quicklist-view) diff --git a/tests/mock_key_grabber.h b/tests/mock_key_grabber.h index 2c271ca56..27bccd181 100644 --- a/tests/mock_key_grabber.h +++ b/tests/mock_key_grabber.h @@ -18,7 +18,7 @@ * */ -#include "KeyGrabber.h" +#include "unity-shared/KeyGrabber.h" #include <gmock/gmock.h> namespace unity diff --git a/tests/test_error_preview.cpp b/tests/test_error_preview.cpp deleted file mode 100644 index a339e0d2c..000000000 --- a/tests/test_error_preview.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2012-2013 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: Diego Sarmentero <diego.sarmentero@canonical.com> - * - */ -#include <list> -#include <gmock/gmock.h> -using namespace testing; - -#include <Nux/Nux.h> -#include <Nux/BaseWindow.h> -#include <unity-shared/StaticCairoText.h> -#include <unity-shared/DashStyle.h> -#include <unity-shared/PreviewStyle.h> -#include <unity-shared/ThumbnailGenerator.h> - -#include <unity-protocol.h> -#include "dash/previews/ErrorPreview.h" -#include "test_utils.h" - -namespace unity -{ - -namespace dash -{ - -namespace previews -{ - -class ErrorPreviewMock : public ErrorPreview -{ - public: - ErrorPreviewMock(dash::Preview::Ptr preview_model) - : ErrorPreview(preview_model){} - ~ErrorPreviewMock(){} - - using ErrorPreview::intro_; - using ErrorPreview::title_; - using ErrorPreview::subtitle_; - using ErrorPreview::purchase_hint_; - using ErrorPreview::purchase_prize_; - using ErrorPreview::purchase_type_; -}; - -class TestErrorPreview : public Test -{ - protected: - TestErrorPreview() : - Test(), - parent_window_(new nux::BaseWindow("TestErrorPayment")) - { - title = "Turning Japanese"; - subtitle = "The vapors"; - header = "Hi test, you purchased in the past from Ubuntu One."; - purchase_prize = "65$"; - purchase_type = "Mp3"; - preview_type = UNITY_PROTOCOL_PREVIEW_PAYMENT_TYPE_ERROR; - - glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_payment_preview_new())); - - unity_protocol_preview_set_title(proto_obj, title.c_str()); - unity_protocol_preview_set_subtitle(proto_obj, subtitle.c_str()); - - unity_protocol_payment_preview_set_header(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), header.c_str()); - unity_protocol_payment_preview_set_purchase_prize(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), purchase_prize.c_str()); - unity_protocol_payment_preview_set_purchase_type(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), purchase_type.c_str()); - unity_protocol_payment_preview_set_preview_type(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), preview_type); - - glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); - - preview_model = dash::Preview::PreviewForVariant(v); - - } - - nux::ObjectPtr<nux::BaseWindow> parent_window_; - dash::Preview::Ptr preview_model; - - // testing data - std::string title; - std::string subtitle; - std::string header; - std::string purchase_prize; - std::string purchase_type; - UnityProtocolPreviewPaymentType preview_type; - - // needed for styles - dash::Style dash_style; -}; - -} // previews - -} // dash - -} // unity diff --git a/tests/test_launcher.cpp b/tests/test_launcher.cpp index d49f886e1..dddba1040 100644 --- a/tests/test_launcher.cpp +++ b/tests/test_launcher.cpp @@ -205,33 +205,6 @@ struct TestWindowCompositor } }; -TEST_F(TestLauncher, TestQuirksDuringDnd) -{ - MockMockLauncherIcon::Ptr first(new MockMockLauncherIcon::Nice); - model_->AddIcon(first); - - MockMockLauncherIcon::Ptr second(new MockMockLauncherIcon::Nice); - model_->AddIcon(second); - - MockMockLauncherIcon::Ptr third(new MockMockLauncherIcon::Nice); - model_->AddIcon(third); - - EXPECT_CALL(*first, ShouldHighlightOnDrag(_)) - .WillRepeatedly(Return(true)); - - EXPECT_CALL(*second, ShouldHighlightOnDrag(_)) - .WillRepeatedly(Return(true)); - - EXPECT_CALL(*third, ShouldHighlightOnDrag(_)) - .WillRepeatedly(Return(false)); - - launcher_->DndStarted(""); - - EXPECT_FALSE(first->GetQuirk(launcher::AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); - EXPECT_FALSE(second->GetQuirk(launcher::AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); - EXPECT_TRUE(third->GetQuirk(launcher::AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); -} - TEST_F(TestLauncher, TestMouseWheelScroll) { MockMockLauncherIcon::Ptr icon(new MockMockLauncherIcon::Nice); @@ -496,17 +469,6 @@ TEST_F(TestLauncher, DragLauncherIconHidesOutsideLauncherEmitsMouseEnter) EXPECT_FALSE(mouse_entered); } -TEST_F(TestLauncher, EdgeReleasesDuringDnd) -{ - auto barrier = std::make_shared<ui::PointerBarrierWrapper>(); - auto event = std::make_shared<ui::BarrierEvent>(0, 0, 0, 100); - - launcher_->DndStarted(""); - - EXPECT_EQ(launcher_->HandleBarrierEvent(barrier, event), - ui::EdgeBarrierSubscriber::Result::NEEDS_RELEASE); -} - TEST_F(TestLauncher, EdgeBarriersIgnoreEvents) { auto const& launcher_geo = launcher_->GetAbsoluteGeometry(); @@ -618,26 +580,6 @@ TEST_F(TestLauncher, DndIsSpecialRequest) EXPECT_FALSE(launcher_->DndIsSpecialRequest("file://full/path/to/MyFile.txt")); } -TEST_F(TestLauncher, AddRequestSignal) -{ - auto const& icons = AddMockIcons(1); - auto const& center = icons[0]->GetCenter(launcher_->monitor()); - launcher_->ProcessDndEnter(); - launcher_->FakeProcessDndMove(center.x, center.y, {"application://MyFile.desktop"}); - - bool add_request = false; - launcher_->add_request.connect([&] (std::string const& uri, AbstractLauncherIcon::Ptr const& drop_icon) { - EXPECT_EQ(drop_icon, icons[0]); - EXPECT_EQ(uri, "application://MyFile.desktop"); - add_request = true; - }); - - launcher_->ProcessDndDrop(center.x, center.y); - launcher_->ProcessDndLeave(); - - EXPECT_TRUE(add_request); -} - TEST_F(TestLauncher, IconStartingPulseValue) { MockMockLauncherIcon::Ptr icon(new MockMockLauncherIcon::Nice); diff --git a/tests/test_launcher_controller.cpp b/tests/test_launcher_controller.cpp index 9495dffff..2f7427138 100644 --- a/tests/test_launcher_controller.cpp +++ b/tests/test_launcher_controller.cpp @@ -1758,46 +1758,6 @@ TEST_F(TestLauncherController, DisconnectWMSignalsOnDestruction) color_property.changed.emit(nux::color::RandomColor()); } -TEST_F(TestLauncherController, DragAndDrop_MultipleLaunchers) -{ - lc.multiple_launchers = true; - uscreen.SetupFakeMultiMonitor(); - lc.options()->hide_mode = LAUNCHER_HIDE_AUTOHIDE; - unsigned monitor = 0; - unsigned old_monitor = -1; - - auto check_fn = [this](int index) { - return lc.launchers()[index]->Hidden(); - }; - - ON_CALL(*xdnd_manager_, Monitor()).WillByDefault(ReturnPointee(&monitor)); - xdnd_manager_->dnd_started.emit("my_awesome_file", monitor); - - for (unsigned i = 0; i < monitors::MAX; ++i) - { - Utils::WaitUntilMSec(std::bind(check_fn, i), i != monitor); - ASSERT_EQ(i != monitor, check_fn(i)); - } - - old_monitor = monitor; - monitor = 3; - xdnd_manager_->monitor_changed.emit("another_file", old_monitor, monitor); - - for (unsigned i = 0; i < monitors::MAX; ++i) - { - Utils::WaitUntilMSec(std::bind(check_fn, i), i != monitor); - ASSERT_EQ(i != monitor, check_fn(i)); - } - - xdnd_manager_->dnd_finished.emit(); - - for (unsigned i = 0; i < monitors::MAX; ++i) - { - Utils::WaitUntilMSec(std::bind(check_fn, i), true); - ASSERT_TRUE(check_fn(i)); - } -} - TEST_F(TestLauncherController, DragAndDrop_SingleLauncher) { lc.multiple_launchers = false; @@ -1824,87 +1784,6 @@ TEST_F(TestLauncherController, DragAndDrop_SingleLauncher) Utils::WaitUntilMSec(check_fn, true); } -TEST_F(TestLauncherController, DragAndDrop_MultipleLaunchers_DesaturateIcons) -{ - lc.multiple_launchers = true; - uscreen.SetupFakeMultiMonitor(); - unsigned monitor = 0; - unsigned old_monitor = -1; - auto const& model = lc.Impl()->model_; - - ON_CALL(*xdnd_manager_, Monitor()).WillByDefault(ReturnPointee(&monitor)); - xdnd_manager_->dnd_started.emit("my_awesome_file", monitor); - - for (auto const& icon : *model) - { - bool is_trash = icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH; - - for (unsigned i = 0; i < monitors::MAX; ++i) - ASSERT_EQ(monitor == i && !is_trash, icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); - } - - old_monitor = monitor; - monitor = 3; - xdnd_manager_->monitor_changed.emit("another_file", old_monitor, monitor); - - for (auto const& icon : *model) - { - bool is_trash = icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH; - - for (unsigned i = 0; i < monitors::MAX; ++i) - ASSERT_EQ(monitor == i && !is_trash, icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); - } - - xdnd_manager_->dnd_finished.emit(); - - for (auto const& icon : *model) - { - for (unsigned i = 0; i < monitors::MAX; ++i) - ASSERT_FALSE(icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); - } -} - -TEST_F(TestLauncherController, DragAndDrop_SingleLauncher_DesaturateIcons) -{ - lc.multiple_launchers = false; - unsigned monitor = 2; - unsigned old_monitor = -1; - uscreen.SetupFakeMultiMonitor(monitor); - lc.options()->hide_mode = LAUNCHER_HIDE_AUTOHIDE; - auto const& model = lc.Impl()->model_; - - ON_CALL(*xdnd_manager_, Monitor()).WillByDefault(ReturnPointee(&monitor)); - xdnd_manager_->dnd_started.emit("my_awesome_file", monitor); - - for (auto const& icon : *model) - { - bool is_trash = icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH; - - for (unsigned i = 0; i < monitors::MAX; ++i) - ASSERT_EQ(monitor == i && !is_trash, icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); - } - - old_monitor = monitor; - monitor = 0; - xdnd_manager_->monitor_changed.emit("another_file", old_monitor, monitor); - - for (auto const& icon : *model) - { - bool is_trash = icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH; - - for (unsigned i = 0; i < monitors::MAX; ++i) - ASSERT_EQ(old_monitor == i && !is_trash, icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); - } - - xdnd_manager_->dnd_finished.emit(); - - for (auto const& icon : *model) - { - for (unsigned i = 0; i < monitors::MAX; ++i) - ASSERT_FALSE(icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); - } -} - TEST_F(TestLauncherController, SetExistingLauncherIconAsFavorite) { const char * desktop_file = "normal-icon.desktop"; diff --git a/tests/test_preview_player.cpp b/tests/test_preview_player.cpp deleted file mode 100644 index df481e9fc..000000000 --- a/tests/test_preview_player.cpp +++ /dev/null @@ -1,230 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* - * Copyright (C) 2013 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: Nick Dedekind <nick.dedekinc@canonical.com> - */ - -#include <gmock/gmock.h> -#include <UnityCore/PreviewPlayer.h> -#include <UnityCore/ConnectionManager.h> -#include <UnityCore/GLibDBusServer.h> -#include <UnityCore/Variant.h> -#include "test_utils.h" -#include "config.h" - -namespace unity -{ - -namespace -{ - const std::string WHITE_NOISE = "file://" BUILDDIR "/tests/data/unity/sounds/whitenoise.mp3"; - - const std::string PLAYER_NAME = "com.canonical.Unity.Lens.Music.PreviewPlayer"; - const std::string PLAYER_PATH = "/com/canonical/Unity/Lens/Music/PreviewPlayer"; - const std::string PLAYER_INTERFACE = - R"(<node> - <interface name="com.canonical.Unity.Lens.Music.PreviewPlayer"> - <method name="Play"> - <arg type="s" name="uri" direction="in"/> - </method> - <method name="Pause"> - </method> - <method name="Resume"> - </method> - <method name="PauseResume"> - </method> - <method name="Stop"> - </method> - <method name="Close"> - </method> - <method name="VideoProperties"> - <arg type="s" name="uri" direction="in"/> - <arg type="a{sv}" name="result" direction="out"/> - </method> - <signal name="Progress"> - <arg type="s" name="uri"/> - <arg type="u" name="state"/> - <arg type="d" name="value"/> - </signal> - </interface> - </node>)"; - - void PlayAndWait(PreviewPlayer* player, std::string const& uri) - { - bool play_returned = false; - auto play_callback = [&play_returned] (glib::Error const& error) { - play_returned = true; - EXPECT_TRUE(!error) << "Error: " << error.Message(); - }; - - bool updated_called = false; - auto updated_callback = [uri, &updated_called] (std::string const& _uri, PlayerState state, double) { - updated_called = true; - EXPECT_EQ(_uri, uri) << "Uri for PLAY not correct (" << _uri << " != " << _uri << ")"; - EXPECT_EQ((int)state, (int)PlayerState::PLAYING) << "Incorrect state returned on PLAY."; - }; - - connection::Wrapper conn(player->updated.connect(updated_callback)); - player->Play(uri, play_callback); - ::Utils::WaitUntilMSec(play_returned, 3000, "PLAY did not return"); - ::Utils::WaitUntilMSec(updated_called, 5000, "Update not called on PLAY"); - } - - void PauseAndWait(PreviewPlayer* player) - { - bool pause_returned = false; - auto callback = [&pause_returned] (glib::Error const& error) { - pause_returned = true; - EXPECT_TRUE(!error) << "Error: " << error.Message(); - }; - - bool updated_called = false; - auto updated_callback = [&updated_called] (std::string const&, PlayerState state, double) { - updated_called = true; - EXPECT_EQ((int)state, (int)PlayerState::PAUSED) << "Incorrect state returned on PAUSE."; - }; - - connection::Wrapper conn(player->updated.connect(updated_callback)); - player->Pause(callback); - ::Utils::WaitUntilMSec(pause_returned, 3000, "PAUSE did not return"); - ::Utils::WaitUntilMSec(updated_called, 5000, "Update not called on PAUSE"); - } - - void ResumeAndWait(PreviewPlayer* player) - { - bool resume_returned = false; - auto callback = [&resume_returned] (glib::Error const& error) { - resume_returned = true; - EXPECT_TRUE(!error) << "Error: " << error.Message(); - }; - - bool updated_called = false; - auto updated_callback = [&updated_called] (std::string const&, PlayerState state, double) { - updated_called = true; - EXPECT_EQ((int)state, (int)PlayerState::PLAYING) << "Incorrect state returned on RESUME."; - }; - - connection::Wrapper conn(player->updated.connect(updated_callback)); - player->Resume(callback); - ::Utils::WaitUntilMSec(resume_returned, 3000, "RESUME did not return"); - ::Utils::WaitUntilMSec(updated_called, 5000, "Update not called on RESUME"); - } - - void StopAndWait(PreviewPlayer* player) - { - bool stop_returned = false; - auto callback = [&stop_returned] (glib::Error const& error) { - stop_returned = true; - EXPECT_TRUE(!error) << "Error: " << error.Message(); - }; - - bool updated_called = false; - auto updated_callback = [&updated_called] (std::string const&, PlayerState state, double) { - updated_called = true; - EXPECT_EQ((int)state, (int)PlayerState::STOPPED) << "Incorrect state returned on STOP."; - }; - - connection::Wrapper conn(player->updated.connect(updated_callback)); - player->Stop(callback); - ::Utils::WaitUntilMSec(stop_returned, 3000, "STOP did not return"); - ::Utils::WaitUntilMSec(updated_called, 5000, "Update not called on STOP"); - } - - struct FakeRemotePlayer - { - typedef std::shared_ptr<FakeRemotePlayer> Ptr; - FakeRemotePlayer() - : server_(PLAYER_NAME) - { - server_.AddObjects(PLAYER_INTERFACE, PLAYER_PATH); - auto object = server_.GetObjects().front(); - - object->SetMethodsCallsHandler([this, object] (std::string const& method, GVariant* parameters) { - if (method == "Play") - { - current_uri_ = glib::Variant(parameters).GetString(); - object->EmitSignal("Progress", g_variant_new("(sud)", current_uri_.c_str(), PlayerState::PLAYING, 0)); - } - else if (method == "Pause") - { - object->EmitSignal("Progress", g_variant_new("(sud)", current_uri_.c_str(), PlayerState::PAUSED, 0)); - } - else if (method == "Resume") - { - object->EmitSignal("Progress", g_variant_new("(sud)", current_uri_.c_str(), PlayerState::PLAYING, 0)); - } - else if (method == "Stop") - { - object->EmitSignal("Progress", g_variant_new("(sud)", current_uri_.c_str(), PlayerState::STOPPED, 0)); - current_uri_ = ""; - } - - return static_cast<GVariant*>(nullptr); - }); - } - - private: - glib::DBusServer server_; - std::string current_uri_; - }; -} - -struct TestPreviewPlayer : testing::Test -{ - static void SetUpTestCase() - { - remote_player_ = std::make_shared<FakeRemotePlayer>(); - } - - static void TearDownTestCase() - { - remote_player_.reset(); - } - - static FakeRemotePlayer::Ptr remote_player_; - PreviewPlayer player; -}; - -FakeRemotePlayer::Ptr TestPreviewPlayer::remote_player_; - -TEST_F(TestPreviewPlayer, TestConstruct) -{ - PreviewPlayer player1; -} - -TEST_F(TestPreviewPlayer, TestPlayerControl) -{ - PlayAndWait(&player, WHITE_NOISE); - - PauseAndWait(&player); - - ResumeAndWait(&player); - - StopAndWait(&player); -} - -TEST_F(TestPreviewPlayer, TestMultiPlayer) -{ - { - PreviewPlayer player2; - PlayAndWait(&player2, WHITE_NOISE); - } - - StopAndWait(&player); -} - - -} // namespace unity \ No newline at end of file diff --git a/tests/test_previews.cpp b/tests/test_previews.cpp deleted file mode 100644 index 3c99a087e..000000000 --- a/tests/test_previews.cpp +++ /dev/null @@ -1,230 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* - * Copyright (C) 2011 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: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> - */ - -#include <list> -#include <algorithm> -#include <gmock/gmock.h> -#include <gio/gio.h> -#include <UnityCore/Variant.h> -#include <UnityCore/Preview.h> -#include <UnityCore/ApplicationPreview.h> -#include <UnityCore/MoviePreview.h> -#include <UnityCore/MusicPreview.h> -#include <unity-protocol.h> - -using namespace std; -using namespace testing; -using namespace unity; -using namespace unity::glib; -using namespace unity::dash; - -namespace -{ - -bool IsVariant(Variant const& variant) -{ - return g_variant_get_type_string(variant) != NULL; -} - -static void g_variant_unref0(gpointer var) -{ - if (var) g_variant_unref((GVariant*)var); -} - -TEST(TestPreviews, DeserializeGeneric) -{ - Object<GIcon> icon(g_icon_new_for_string("accessories", NULL)); - Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_generic_preview_new())); - unity_protocol_preview_set_title(proto_obj, "Title"); - unity_protocol_preview_set_subtitle(proto_obj, "Subtitle"); - unity_protocol_preview_set_description(proto_obj, "Description"); - unity_protocol_preview_set_image(proto_obj, icon); - unity_protocol_preview_set_image_source_uri(proto_obj, "Source"); - - Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), - glib::StealRef()); - EXPECT_TRUE(IsVariant(v)); - - Preview::Ptr preview = Preview::PreviewForVariant(v); - EXPECT_TRUE(preview != nullptr); - - EXPECT_EQ(preview->renderer_name, "preview-generic"); - EXPECT_EQ(preview->title, "Title"); - EXPECT_EQ(preview->subtitle, "Subtitle"); - EXPECT_EQ(preview->description, "Description"); - EXPECT_EQ(preview->image_source_uri, "Source"); - EXPECT_TRUE(g_icon_equal(preview->image(), icon) != FALSE); -} - -TEST(TestPreviews, DeserializeGenericWithMeta) -{ - Object<GIcon> icon(g_icon_new_for_string("accessories", NULL)); - Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_generic_preview_new())); - unity_protocol_preview_set_title(proto_obj, "Title"); - unity_protocol_preview_set_subtitle(proto_obj, "Subtitle"); - unity_protocol_preview_set_description(proto_obj, "Description"); - unity_protocol_preview_set_image(proto_obj, icon); - unity_protocol_preview_set_image_source_uri(proto_obj, "Source"); - - GHashTable* hints = g_hash_table_new_full(g_str_hash, g_direct_equal, g_free, g_variant_unref0); - g_hash_table_insert(hints, g_strdup("extra-text"), g_variant_new_string("Foo")); - unity_protocol_preview_add_action(proto_obj, "action1", "Action #1", NULL, 0); - unity_protocol_preview_add_action_with_hints(proto_obj, "action2", "Action #2", NULL, 0, hints); - unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("i", 34)); - unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint")); - - Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), - glib::StealRef()); - EXPECT_TRUE(IsVariant(v)); - - Preview::Ptr preview = Preview::PreviewForVariant(v); - EXPECT_TRUE(preview != nullptr); - - EXPECT_EQ(preview->renderer_name, "preview-generic"); - EXPECT_EQ(preview->title, "Title"); - EXPECT_EQ(preview->subtitle, "Subtitle"); - EXPECT_EQ(preview->description, "Description"); - EXPECT_TRUE(g_icon_equal(preview->image(), icon) != FALSE); - EXPECT_EQ(preview->image_source_uri, "Source"); - - auto actions = preview->GetActions(); - auto info_hints = preview->GetInfoHints(); - - EXPECT_EQ(actions.size(), 2u); - - auto action1 = actions[0]; - EXPECT_EQ(action1->id, "action1"); - EXPECT_EQ(action1->display_name, "Action #1"); - EXPECT_EQ(action1->icon_hint, ""); - EXPECT_EQ(action1->layout_hint, 0); - EXPECT_EQ(action1->extra_text, ""); - - auto action2 = actions[1]; - EXPECT_EQ(action2->id, "action2"); - EXPECT_EQ(action2->display_name, "Action #2"); - EXPECT_EQ(action2->icon_hint, ""); - EXPECT_EQ(action2->extra_text, "Foo"); - - EXPECT_EQ(info_hints.size(), 2u); - auto hint1 = info_hints[0]; - EXPECT_EQ(hint1->id, "hint1"); - EXPECT_EQ(hint1->display_name, "Hint 1"); - EXPECT_EQ(hint1->icon_hint, ""); - EXPECT_EQ(hint1->value.GetInt32(), 34); - auto hint2 = info_hints[1]; - EXPECT_EQ(hint2->id, "hint2"); - EXPECT_EQ(hint2->display_name, "Hint 2"); - EXPECT_EQ(hint2->icon_hint, ""); - EXPECT_EQ(hint2->value.GetString(), "string hint"); -} - -TEST(TestPreviews, DeserializeApplication) -{ - Object<GIcon> icon(g_icon_new_for_string("application", NULL)); - Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_application_preview_new())); - unity_protocol_preview_set_title(proto_obj, "Title"); - unity_protocol_preview_set_subtitle(proto_obj, "Subtitle"); - unity_protocol_preview_set_description(proto_obj, "Description"); - unity_protocol_preview_set_image(proto_obj, icon); - auto app_proto_obj = glib::object_cast<UnityProtocolApplicationPreview>(proto_obj); - unity_protocol_application_preview_set_last_update(app_proto_obj, "2012/06/13"); - unity_protocol_application_preview_set_copyright(app_proto_obj, "(c) Canonical"); - unity_protocol_application_preview_set_license(app_proto_obj, "GPLv3"); - unity_protocol_application_preview_set_app_icon(app_proto_obj, icon); - unity_protocol_application_preview_set_rating(app_proto_obj, 4.0); - unity_protocol_application_preview_set_num_ratings(app_proto_obj, 12); - - Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), - glib::StealRef()); - EXPECT_TRUE(IsVariant(v)); - - Preview::Ptr base_preview = Preview::PreviewForVariant(v); - ApplicationPreview::Ptr preview = std::dynamic_pointer_cast<ApplicationPreview>(base_preview); - EXPECT_TRUE(preview != nullptr); - - EXPECT_EQ(preview->renderer_name, "preview-application"); - EXPECT_EQ(preview->title, "Title"); - EXPECT_EQ(preview->subtitle, "Subtitle"); - EXPECT_EQ(preview->description, "Description"); - EXPECT_TRUE(g_icon_equal(preview->image(), icon) != FALSE); - EXPECT_EQ(preview->last_update, "2012/06/13"); - EXPECT_EQ(preview->copyright, "(c) Canonical"); - EXPECT_EQ(preview->license, "GPLv3"); - EXPECT_TRUE(g_icon_equal(preview->app_icon(), icon) != FALSE); - EXPECT_EQ(preview->rating, 4.0); - EXPECT_EQ(preview->num_ratings, static_cast<unsigned>(12)); -} - -TEST(TestPreviews, DeserializeMovie) -{ - Object<GIcon> icon(g_icon_new_for_string("movie", NULL)); - Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_movie_preview_new())); - unity_protocol_preview_set_title(proto_obj, "Title"); - unity_protocol_preview_set_subtitle(proto_obj, "Subtitle"); - unity_protocol_preview_set_description(proto_obj, "Description"); - unity_protocol_preview_set_image(proto_obj, icon); - auto movie_proto_obj = glib::object_cast<UnityProtocolMoviePreview>(proto_obj); - unity_protocol_movie_preview_set_year(movie_proto_obj, "2012"); - unity_protocol_movie_preview_set_rating(movie_proto_obj, 4.0); - unity_protocol_movie_preview_set_num_ratings(movie_proto_obj, 12); - - Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), - glib::StealRef()); - EXPECT_TRUE(IsVariant(v)); - - Preview::Ptr base_preview = Preview::PreviewForVariant(v); - MoviePreview::Ptr preview = std::dynamic_pointer_cast<MoviePreview>(base_preview); - EXPECT_TRUE(preview != nullptr); - - EXPECT_EQ(preview->renderer_name, "preview-movie"); - EXPECT_EQ(preview->title, "Title"); - EXPECT_EQ(preview->subtitle, "Subtitle"); - EXPECT_EQ(preview->description, "Description"); - EXPECT_TRUE(g_icon_equal(preview->image(), icon) != FALSE); - EXPECT_EQ(preview->year, "2012"); - EXPECT_EQ(preview->rating, 4.0); - EXPECT_EQ(preview->num_ratings, static_cast<unsigned int>(12)); -} - -TEST(TestPreviews, DeserializeMusic) -{ - Object<GIcon> icon(g_icon_new_for_string("music", NULL)); - Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_music_preview_new())); - unity_protocol_preview_set_title(proto_obj, "Title"); - unity_protocol_preview_set_subtitle(proto_obj, "Subtitle"); - unity_protocol_preview_set_description(proto_obj, "Description"); - unity_protocol_preview_set_image(proto_obj, icon); - auto music_proto_obj = glib::object_cast<UnityProtocolMusicPreview>(proto_obj); - - Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), - glib::StealRef()); - EXPECT_TRUE(IsVariant(v)); - - Preview::Ptr base_preview = Preview::PreviewForVariant(v); - MusicPreview::Ptr preview = std::dynamic_pointer_cast<MusicPreview>(base_preview); - EXPECT_TRUE(preview != nullptr); - - EXPECT_EQ(preview->renderer_name, "preview-music"); - EXPECT_EQ(preview->title, "Title"); - EXPECT_EQ(preview->subtitle, "Subtitle"); - EXPECT_EQ(preview->description, "Description"); - EXPECT_TRUE(g_icon_equal(preview->image(), icon) != FALSE); -} - -} // Namespace diff --git a/tests/test_previews_application.cpp b/tests/test_previews_application.cpp deleted file mode 100644 index 82afc2937..000000000 --- a/tests/test_previews_application.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// -*- 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: Nick Dedekind <nick.dedekinc@canonical.com> - */ - -#include <list> -#include <gmock/gmock.h> -using namespace testing; - -#include <Nux/Nux.h> -#include <Nux/BaseWindow.h> -#include <unity-shared/StaticCairoText.h> -#include <unity-shared/DashStyle.h> -#include <unity-shared/PreviewStyle.h> -#include <unity-shared/ThumbnailGenerator.h> - -#include <unity-protocol.h> -#include "UnityCore/ApplicationPreview.h" -#include "dash/previews/ApplicationPreview.h" -#include "dash/previews/PreviewInfoHintWidget.h" -#include "dash/previews/PreviewRatingsWidget.h" -#include "dash/previews/ActionButton.h" -#include "test_utils.h" -using namespace unity; -using namespace unity::dash; - -namespace -{ - -class MockApplicationPreview : public previews::ApplicationPreview -{ -public: - typedef nux::ObjectPtr<MockApplicationPreview> Ptr; - - MockApplicationPreview(dash::Preview::Ptr preview_model) - : ApplicationPreview(preview_model) - {} - - using ApplicationPreview::title_; - using ApplicationPreview::subtitle_; - using ApplicationPreview::license_; - using ApplicationPreview::last_update_; - using ApplicationPreview::copywrite_; - using ApplicationPreview::description_; - using ApplicationPreview::action_buttons_; - using ApplicationPreview::preview_info_hints_; - using ApplicationPreview::app_rating_; -}; - -class TestPreviewApplication : public Test -{ -public: - TestPreviewApplication() - : parent_window_(new nux::BaseWindow("TestPreviewApplication")) - { - glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_application_preview_new())); - - GHashTable* action_hints1(g_hash_table_new(g_direct_hash, g_direct_equal)); - g_hash_table_insert (action_hints1, g_strdup ("extra-text"), g_variant_new_string("£30.99")); - - unity_protocol_application_preview_set_app_icon(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), g_icon_new_for_string(TESTDATADIR "/bfb.png", NULL)); - unity_protocol_application_preview_set_license(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "License & special char"); - unity_protocol_application_preview_set_copyright(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "Copywrite & special char"); - unity_protocol_application_preview_set_last_update(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "11th Apr 2012"); - unity_protocol_application_preview_set_rating(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 0.8); - unity_protocol_application_preview_set_num_ratings(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 12); - - unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); - unity_protocol_preview_set_title(proto_obj, "Application Title & special char"); - unity_protocol_preview_set_subtitle(proto_obj, "Application Subtitle > special char"); - unity_protocol_preview_set_description(proto_obj, "Application Desctiption < special char"); - unity_protocol_preview_add_action(proto_obj, "action1", "Action 1", NULL, 0); - unity_protocol_preview_add_action_with_hints(proto_obj, "action2", "Action 2", NULL, 0, action_hints1); - unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("s", "string hint 1")); - unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint 2")); - unity_protocol_preview_add_info_hint(proto_obj, "hint3", "Hint 3", NULL, g_variant_new("i", 12)); - - glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); - preview_model_ = dash::Preview::PreviewForVariant(v); - - g_hash_table_unref(action_hints1); - } - - nux::ObjectPtr<nux::BaseWindow> parent_window_; - dash::Preview::Ptr preview_model_; - - previews::Style previews_style; - dash::Style dash_style; - ThumbnailGenerator thumbnail_generator; -}; - -TEST_F(TestPreviewApplication, TestCreate) -{ - previews::Preview::Ptr preview_view = previews::Preview::PreviewForModel(preview_model_); - - EXPECT_TRUE(dynamic_cast<previews::ApplicationPreview*>(preview_view.GetPointer()) != NULL); -} - -TEST_F(TestPreviewApplication, TestUIValues) -{ - MockApplicationPreview::Ptr preview_view(new MockApplicationPreview(preview_model_)); - - EXPECT_EQ(preview_view->title_->GetText(), "Application Title & special char"); - EXPECT_EQ(preview_view->subtitle_->GetText(), "Application Subtitle > special char"); - EXPECT_EQ(preview_view->description_->GetText(), "Application Desctiption < special char"); - - EXPECT_EQ(preview_view->action_buttons_.size(), 2u); - - if (preview_view->action_buttons_.size() >= 2) - { - auto iter = preview_view->action_buttons_.begin(); - if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) - { - ActionButton *action = static_cast<ActionButton*>(*iter); - EXPECT_EQ(action->GetLabel(), "Action 1"); - EXPECT_EQ(action->GetExtraText(), ""); - } - iter++; - if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) - { - ActionButton *action = static_cast<ActionButton*>(*iter); - EXPECT_EQ(action->GetLabel(), "Action 2"); - EXPECT_EQ(action->GetExtraText(), "£30.99"); - } - } -} - -} diff --git a/tests/test_previews_generic.cpp b/tests/test_previews_generic.cpp deleted file mode 100644 index c10c99e1a..000000000 --- a/tests/test_previews_generic.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// -*- 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: Nick Dedekind <nick.dedekinc@canonical.com> - */ - -#include <list> -#include <gmock/gmock.h> -using namespace testing; - -#include <Nux/Nux.h> -#include <Nux/BaseWindow.h> -#include <unity-shared/StaticCairoText.h> -#include <unity-shared/DashStyle.h> -#include <unity-shared/PreviewStyle.h> -#include <unity-shared/ThumbnailGenerator.h> - -#include <unity-protocol.h> -#include "UnityCore/GenericPreview.h" -#include "dash/previews/GenericPreview.h" -#include "dash/previews/ActionButton.h" -#include "test_utils.h" -using namespace unity; -using namespace unity::dash; - -namespace -{ - -class MockGenericPreview : public previews::GenericPreview -{ -public: - typedef nux::ObjectPtr<MockGenericPreview> Ptr; - - MockGenericPreview(dash::Preview::Ptr preview_model) - : GenericPreview(preview_model) - {} - - using GenericPreview::title_; - using GenericPreview::subtitle_; - using GenericPreview::description_; - using GenericPreview::action_buttons_; - using GenericPreview::preview_info_hints_; -}; - -class TestPreviewGeneric : public Test -{ -public: - TestPreviewGeneric() - : parent_window_(new nux::BaseWindow("TestPreviewGeneric")) - { - glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_generic_preview_new())); - - GHashTable* action_hints1(g_hash_table_new(g_direct_hash, g_direct_equal)); - g_hash_table_insert (action_hints1, g_strdup ("extra-text"), g_variant_new_string("2.00")); - - unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); - unity_protocol_preview_set_title(proto_obj, "Generic Title & special char"); - unity_protocol_preview_set_subtitle(proto_obj, "Generic Subtitle > special char"); - unity_protocol_preview_set_description(proto_obj, "Generic Desctiption < special char"); - unity_protocol_preview_add_action(proto_obj, "action1", "Action 1", NULL, 0); - unity_protocol_preview_add_action_with_hints(proto_obj, "action2", "Action 2", NULL, 0, action_hints1); - unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("s", "string hint 1")); - unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint 2")); - unity_protocol_preview_add_info_hint(proto_obj, "hint3", "Hint 3", NULL, g_variant_new("i", 12)); - - glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); - preview_model_ = dash::Preview::PreviewForVariant(v); - - g_hash_table_unref(action_hints1); - } - - nux::ObjectPtr<nux::BaseWindow> parent_window_; - dash::Preview::Ptr preview_model_; - - previews::Style panel_style; - dash::Style dash_style; - ThumbnailGenerator thumbnail_generator; -}; - -TEST_F(TestPreviewGeneric, TestCreate) -{ - previews::Preview::Ptr preview_view = previews::Preview::PreviewForModel(preview_model_); - - EXPECT_TRUE(dynamic_cast<previews::GenericPreview*>(preview_view.GetPointer()) != NULL); -} - -TEST_F(TestPreviewGeneric, TestUIValues) -{ - MockGenericPreview::Ptr preview_view(new MockGenericPreview(preview_model_)); - - EXPECT_EQ(preview_view->title_->GetText(), "Generic Title & special char"); - EXPECT_EQ(preview_view->subtitle_->GetText(), "Generic Subtitle > special char"); - EXPECT_EQ(preview_view->description_->GetText(), "Generic Desctiption < special char"); - - EXPECT_EQ(preview_view->action_buttons_.size(), 2u); - - if (preview_view->action_buttons_.size() >= 2) - { - auto iter = preview_view->action_buttons_.begin(); - if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) - { - ActionButton *action = static_cast<ActionButton*>(*iter); - EXPECT_EQ(action->GetLabel(), "Action 1"); - EXPECT_EQ(action->GetExtraText(), ""); - } - iter++; - if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) - { - ActionButton *action = static_cast<ActionButton*>(*iter); - EXPECT_EQ(action->GetLabel(), "Action 2"); - EXPECT_EQ(action->GetExtraText(), "2.00"); - } - } -} - -} diff --git a/tests/test_previews_movie.cpp b/tests/test_previews_movie.cpp deleted file mode 100644 index ac3b7e1cb..000000000 --- a/tests/test_previews_movie.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// -*- 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: Nick Dedekind <nick.dedekinc@canonical.com> - */ - -#include <list> -#include <gmock/gmock.h> -using namespace testing; - -#include <Nux/Nux.h> -#include <Nux/BaseWindow.h> -#include <unity-shared/StaticCairoText.h> -#include <unity-shared/DashStyle.h> -#include <unity-shared/PreviewStyle.h> -#include <unity-shared/ThumbnailGenerator.h> - -#include <unity-protocol.h> -#include "UnityCore/MoviePreview.h" -#include "dash/previews/MoviePreview.h" -#include "dash/previews/PreviewInfoHintWidget.h" -#include "dash/previews/PreviewRatingsWidget.h" -#include "dash/previews/ActionButton.h" -#include "test_utils.h" -using namespace unity; -using namespace unity::dash; - -namespace -{ - -class MockMoviePreview : public previews::MoviePreview -{ -public: - typedef nux::ObjectPtr<MockMoviePreview> Ptr; - - MockMoviePreview(dash::Preview::Ptr preview_model) - : MoviePreview(preview_model) - {} - - using MoviePreview::title_; - using MoviePreview::subtitle_; - using MoviePreview::description_; - using MoviePreview::action_buttons_; - using MoviePreview::preview_info_hints_; - using MoviePreview::rating_; -}; - -class TestPreviewMovie : public Test -{ -public: - TestPreviewMovie() - : parent_window_(new nux::BaseWindow("TestPreviewMovie")) {} - - void create_preview_model(double rating) - { - glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_movie_preview_new())); - - GHashTable* action_hints1(g_hash_table_new(g_direct_hash, g_direct_equal)); - g_hash_table_insert (action_hints1, g_strdup ("extra-text"), g_variant_new_string("£1.00")); - - unity_protocol_movie_preview_set_rating(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), rating); - unity_protocol_movie_preview_set_num_ratings(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), 12); - - unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); - unity_protocol_preview_set_title(proto_obj, "Movie Title & special char"); - unity_protocol_preview_set_subtitle(proto_obj, "Movie Subtitle > special char"); - unity_protocol_preview_set_description(proto_obj, "Movie Desctiption < special char"); - unity_protocol_preview_add_action(proto_obj, "action1", "Action 1", NULL, 0); - unity_protocol_preview_add_action_with_hints(proto_obj, "action2", "Action 2", NULL, 0, action_hints1); - unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("s", "string hint 1")); - unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint 2")); - unity_protocol_preview_add_info_hint(proto_obj, "hint3", "Hint 3", NULL, g_variant_new("i", 12)); - - glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); - preview_model_ = dash::Preview::PreviewForVariant(v); - - g_hash_table_unref(action_hints1); - } - - nux::ObjectPtr<nux::BaseWindow> parent_window_; - dash::Preview::Ptr preview_model_; - - previews::Style panel_style; - dash::Style dash_style; - ThumbnailGenerator thumbnail_generator; -}; - -TEST_F(TestPreviewMovie, TestCreate) -{ - create_preview_model(0.8); - previews::Preview::Ptr preview_view = previews::Preview::PreviewForModel(preview_model_); - - EXPECT_TRUE(dynamic_cast<previews::MoviePreview*>(preview_view.GetPointer()) != NULL); -} - -TEST_F(TestPreviewMovie, TestUIValues) -{ - create_preview_model(0.8); - MockMoviePreview::Ptr preview_view(new MockMoviePreview(preview_model_)); - - EXPECT_EQ(preview_view->title_->GetText(), "Movie Title & special char"); - EXPECT_EQ(preview_view->subtitle_->GetText(), "Movie Subtitle > special char"); - EXPECT_EQ(preview_view->description_->GetText(), "Movie Desctiption < special char"); - - EXPECT_EQ(preview_view->rating_->GetRating(), 0.8f); - EXPECT_EQ(preview_view->action_buttons_.size(), 2u); - - if (preview_view->action_buttons_.size() >= 2) - { - auto iter = preview_view->action_buttons_.begin(); - if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) - { - ActionButton *action = static_cast<ActionButton*>(*iter); - EXPECT_EQ(action->GetLabel(), "Action 1"); - EXPECT_EQ(action->GetExtraText(), ""); - } - iter++; - if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) - { - ActionButton *action = static_cast<ActionButton*>(*iter); - EXPECT_EQ(action->GetLabel(), "Action 2"); - EXPECT_EQ(action->GetExtraText(), "£1.00"); - } - } -} - -TEST_F(TestPreviewMovie, TestHideRatings) -{ - create_preview_model(-1); - MockMoviePreview::Ptr preview_view(new MockMoviePreview(preview_model_)); - - EXPECT_EQ(preview_view->rating_, NULL); -} - - -} diff --git a/tests/test_previews_music.cpp b/tests/test_previews_music.cpp deleted file mode 100644 index 4398ec292..000000000 --- a/tests/test_previews_music.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// -*- 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: Nick Dedekind <nick.dedekinc@canonical.com> - */ - -#include <list> -#include <gmock/gmock.h> -using namespace testing; - -#include <Nux/Nux.h> -#include <Nux/BaseWindow.h> -#include <unity-shared/StaticCairoText.h> -#include <unity-shared/DashStyle.h> -#include <unity-shared/PreviewStyle.h> -#include <unity-shared/ThumbnailGenerator.h> - -#include <unity-protocol.h> -#include "UnityCore/MusicPreview.h" -#include "dash/previews/MusicPreview.h" -#include "dash/previews/PreviewInfoHintWidget.h" -#include "dash/previews/PreviewRatingsWidget.h" -#include "dash/previews/ActionButton.h" -#include "test_utils.h" -using namespace unity; -using namespace unity::dash; - -namespace -{ - -class MockMusicPreview : public previews::MusicPreview -{ -public: - typedef nux::ObjectPtr<MockMusicPreview> Ptr; - - MockMusicPreview(dash::Preview::Ptr preview_model) - : MusicPreview(preview_model) - {} - - using MusicPreview::title_; - using MusicPreview::subtitle_; - using MusicPreview::action_buttons_; - using MusicPreview::preview_info_hints_; -}; - -class TestPreviewMusic : public Test -{ -public: - TestPreviewMusic() - : parent_window_(new nux::BaseWindow("TestPreviewMusic")) - { - glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_music_preview_new())); - - GHashTable* action_hints1(g_hash_table_new(g_direct_hash, g_direct_equal)); - g_hash_table_insert (action_hints1, g_strdup ("extra-text"), g_variant_new_string("£3.99")); - - unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); - unity_protocol_preview_set_title(proto_obj, "Music Title & special char"); - unity_protocol_preview_set_subtitle(proto_obj, "Music Subtitle > special char"); - unity_protocol_preview_set_description(proto_obj, "Music Desctiption < special char"); - unity_protocol_preview_add_action(proto_obj, "action1", "Action 1", NULL, 0); - unity_protocol_preview_add_action_with_hints(proto_obj, "action2", "Action 2", NULL, 0, action_hints1); - unity_protocol_preview_add_action(proto_obj, "action3", "Action 3", NULL, 0); - unity_protocol_preview_add_action(proto_obj, "action4", "Action 4", NULL, 0); - unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("s", "string hint 1")); - unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint 2")); - unity_protocol_preview_add_info_hint(proto_obj, "hint3", "Hint 3", NULL, g_variant_new("i", 12)); - - glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); - preview_model_ = dash::Preview::PreviewForVariant(v); - - g_hash_table_unref(action_hints1); - } - - nux::ObjectPtr<nux::BaseWindow> parent_window_; - dash::Preview::Ptr preview_model_; - - previews::Style panel_style; - dash::Style dash_style; - ThumbnailGenerator thumbnail_generator; -}; - -TEST_F(TestPreviewMusic, TestCreate) -{ - previews::Preview::Ptr preview_view = previews::Preview::PreviewForModel(preview_model_); - - EXPECT_TRUE(dynamic_cast<previews::MusicPreview*>(preview_view.GetPointer()) != NULL); -} - -TEST_F(TestPreviewMusic, TestUIValues) -{ - MockMusicPreview::Ptr preview_view(new MockMusicPreview(preview_model_)); - - EXPECT_EQ(preview_view->title_->GetText(), "Music Title & special char"); - EXPECT_EQ(preview_view->subtitle_->GetText(), "Music Subtitle > special char"); - - EXPECT_EQ(preview_view->action_buttons_.size(), 4u); - - if (preview_view->action_buttons_.size() >= 2) - { - auto iter = preview_view->action_buttons_.begin(); - if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) - { - ActionButton *action = static_cast<ActionButton*>(*iter); - EXPECT_EQ(action->GetLabel(), "Action 1"); - EXPECT_EQ(action->GetExtraText(), ""); - } - iter++; - if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) - { - ActionButton *action = static_cast<ActionButton*>(*iter); - EXPECT_EQ(action->GetLabel(), "Action 2"); - EXPECT_EQ(action->GetExtraText(), "£3.99"); - } - } -} - -} diff --git a/tests/test_previews_music_payment.cpp b/tests/test_previews_music_payment.cpp deleted file mode 100644 index daa6a3b47..000000000 --- a/tests/test_previews_music_payment.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* - * Copyright (C) 2012-2013 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: Manuel de la Pena <manuel.delapena@canonical.com> - */ - -#include <list> -#include <gmock/gmock.h> - -#include <Nux/Nux.h> -#include <Nux/VLayout.h> -#include <Nux/BaseWindow.h> -#include <unity-shared/StaticCairoText.h> -#include <unity-shared/CoverArt.h> -#include <unity-shared/DashStyle.h> -#include <unity-shared/PreviewStyle.h> -#include <unity-shared/ThumbnailGenerator.h> - -#include <unity-protocol.h> -#include "dash/previews/MusicPaymentPreview.h" -#include "test_utils.h" - -using namespace testing; - -namespace unity -{ - -namespace dash -{ - -namespace previews -{ - -class MockedMusicPaymentPreview : public MusicPaymentPreview -{ -public: - typedef nux::ObjectPtr<MockedMusicPaymentPreview> Ptr; - - MockedMusicPaymentPreview(dash::Preview::Ptr preview_model) - : MusicPaymentPreview(preview_model) - {} - - using MusicPaymentPreview::image_; - using MusicPaymentPreview::intro_; - using MusicPaymentPreview::title_; - using MusicPaymentPreview::subtitle_; - using MusicPaymentPreview::email_label_; - using MusicPaymentPreview::email_; - using MusicPaymentPreview::payment_label_; - using MusicPaymentPreview::payment_; - using MusicPaymentPreview::password_label_; - using MusicPaymentPreview::password_entry_; - using MusicPaymentPreview::purchase_hint_; - using MusicPaymentPreview::purchase_prize_; - using MusicPaymentPreview::purchase_type_; - using MusicPaymentPreview::change_payment_; - using MusicPaymentPreview::forgotten_password_; - using MusicPaymentPreview::error_label_; - using MusicPaymentPreview::form_layout_; - using MusicPaymentPreview::SetupViews; -}; - -class TestMusicPaymentPreview : public ::testing::Test -{ - protected: - TestMusicPaymentPreview() : - Test(), - parent_window_(new nux::BaseWindow("TestPreviewMusicPayment")) - { - title = "Turning Japanese"; - subtitle = "The vapors"; - header = "Hi test, you purchased in the past from Ubuntu One."; - email = "test@canonical.com"; - payment_method = "*** *** ** 12"; - purchase_prize = "65$"; - purchase_type = "Mp3"; - preview_type = UNITY_PROTOCOL_PREVIEW_PAYMENT_TYPE_MUSIC; - - glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_payment_preview_new())); - - unity_protocol_preview_set_title(proto_obj, title.c_str()); - unity_protocol_preview_set_subtitle(proto_obj, subtitle.c_str()); - unity_protocol_preview_add_action(proto_obj, "change_payment_method", "Change payment", NULL, 0); - unity_protocol_preview_add_action(proto_obj, "forgot_password", "Forgot password", NULL, 0); - unity_protocol_preview_add_action(proto_obj, "cancel_purchase", "Cancel", NULL, 0); - unity_protocol_preview_add_action(proto_obj, "purchase_album", "Purchase", NULL, 0); - - - unity_protocol_payment_preview_set_header(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), header.c_str()); - unity_protocol_payment_preview_set_email(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), email.c_str()); - unity_protocol_payment_preview_set_payment_method(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), payment_method.c_str()); - unity_protocol_payment_preview_set_purchase_prize(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), purchase_prize.c_str()); - unity_protocol_payment_preview_set_purchase_type(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), purchase_type.c_str()); - unity_protocol_payment_preview_set_preview_type(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), preview_type); - - glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); - - preview_model = dash::Preview::PreviewForVariant(v); - } - - nux::ObjectPtr<nux::BaseWindow> parent_window_; - dash::Preview::Ptr preview_model; - - // testing data - std::string title; - std::string subtitle; - std::string header; - std::string email; - std::string payment_method; - std::string purchase_prize; - std::string purchase_type; - UnityProtocolPreviewPaymentType preview_type; - - // needed for styles - previews::Style previews_style; - dash::Style dash_style; -}; - -TEST_F(TestMusicPaymentPreview, TestContentLoading) -{ - MockedMusicPaymentPreview::Ptr preview_view(new MockedMusicPaymentPreview(preview_model)); - - EXPECT_EQ(preview_view->title_->GetText(), title); - EXPECT_EQ(preview_view->subtitle_->GetText(), subtitle); - EXPECT_EQ(preview_view->intro_->GetText(), header); - EXPECT_EQ(preview_view->email_->GetText(), email); - EXPECT_EQ(preview_view->payment_->GetText(), payment_method); - EXPECT_EQ(preview_view->purchase_type_->GetText(), purchase_type); - EXPECT_EQ(preview_view->purchase_prize_->GetText(), purchase_prize); -} - - -} // previews - -} // dash - -} // unity diff --git a/tests/test_previews_payment.cpp b/tests/test_previews_payment.cpp deleted file mode 100644 index 53f541743..000000000 --- a/tests/test_previews_payment.cpp +++ /dev/null @@ -1,184 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* - * Copyright (C) 2012-2013 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: Manuel de la Pena <manuel.delapena@canonical.com> - */ -#include <list> -#include <gmock/gmock.h> - -#include <Nux/Nux.h> -#include <Nux/Layout.h> -#include <Nux/VLayout.h> -#include <Nux/BaseWindow.h> -#include <unity-shared/StaticCairoText.h> -#include <unity-shared/DashStyle.h> -#include <unity-shared/PreviewStyle.h> -#include <unity-shared/ThumbnailGenerator.h> -#include <unity-shared/CoverArt.h> - -#include <unity-protocol.h> -#include "dash/previews/PaymentPreview.h" -#include "dash/previews/ActionButton.h" -#include "dash/previews/ActionLink.h" -#include "test_utils.h" - -using namespace testing; -using ::testing::Return; - -namespace unity -{ - -namespace dash -{ - -namespace previews -{ - -class NonAbstractPreview : public PaymentPreview -{ -public: - NonAbstractPreview(dash::Preview::Ptr preview_model) - : PaymentPreview(preview_model) - {} - - virtual nux::Layout* GetTitle() - { - return new nux::VLayout(); - } - - virtual nux::Layout* GetPrice() - { - return new nux::VLayout(); - } - - virtual nux::Layout* GetBody() - { - return new nux::VLayout(); - } - - virtual nux::Layout* GetFooter() - { - return new nux::VLayout(); - } - - virtual void OnActionActivated(ActionButton* button, std::string const& id) - { - // do nothing - } - - virtual void OnActionLinkActivated(ActionLink* link, std::string const& id) - { - // do nothing - } - - virtual void PreLayoutManagement() - { - // do nothing - } - - virtual void LoadActions() - { - // do nothing - } - - using PaymentPreview::GetHeader; - using PaymentPreview::full_data_layout_; - using PaymentPreview::content_data_layout_; - using PaymentPreview::overlay_layout_; - using PaymentPreview::header_layout_; - using PaymentPreview::body_layout_; - using PaymentPreview::footer_layout_; - using PaymentPreview::SetupViews; - -}; - -class MockedPaymentPreview : public NonAbstractPreview -{ -public: - typedef nux::ObjectPtr<MockedPaymentPreview> Ptr; - - MockedPaymentPreview(dash::Preview::Ptr preview_model) - : NonAbstractPreview(preview_model) - {} - - // Mock methods that should be implemented so that we can assert that they are - // called in the correct moments. - MOCK_METHOD0(GetTitle, nux::Layout*()); - MOCK_METHOD0(GetPrice, nux::Layout*()); - MOCK_METHOD0(GetBody, nux::Layout*()); - MOCK_METHOD0(GetFooter, nux::Layout*()); - MOCK_METHOD2(OnActionActivated, void(unity::dash::ActionButton*, std::string)); - MOCK_METHOD2(OnActionLinkActivated, void(unity::dash::ActionLink*, std::string)); - MOCK_METHOD0(PreLayoutManagement, void()); - MOCK_METHOD0(LoadActions, void()); - -}; - -class TestPaymentPreview : public ::testing::Test -{ - protected: - TestPaymentPreview() : Test() - { - glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_payment_preview_new())); - // we are not testing how the info is really used is more asserting the method calls, we do not add any data then - glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); - - preview_model = dash::Preview::PreviewForVariant(v); - - preview = new MockedPaymentPreview(preview_model); - - } - nux::ObjectPtr<MockedPaymentPreview> preview; - dash::Preview::Ptr preview_model; - - // needed for styles - previews::Style previews_style; - dash::Style dash_style; -}; - -TEST_F(TestPaymentPreview, GetHeaderCallsCorrectMethods) -{ - ON_CALL(*preview.GetPointer(), GetTitle()).WillByDefault(Return(new nux::VLayout())); - EXPECT_CALL(*preview.GetPointer(), GetTitle()).Times(1); - - ON_CALL(*preview.GetPointer(), GetPrice()).WillByDefault(Return(new nux::VLayout())); - EXPECT_CALL(*preview.GetPointer(), GetPrice()).Times(1); - - preview->GetHeader()->UnReference(); -} - -TEST_F(TestPaymentPreview, SetupViewsCallCorrectMethods) -{ - ON_CALL(*preview.GetPointer(), GetTitle()).WillByDefault(Return(new nux::VLayout())); - EXPECT_CALL(*preview.GetPointer(), GetTitle()).Times(1); - - ON_CALL(*preview.GetPointer(), GetPrice()).WillByDefault(Return(new nux::VLayout())); - EXPECT_CALL(*preview.GetPointer(), GetPrice()).Times(1); - - ON_CALL(*preview.GetPointer(), GetBody()).WillByDefault(Return(new nux::VLayout())); - EXPECT_CALL(*preview.GetPointer(), GetBody()).Times(1); - - ON_CALL(*preview.GetPointer(), GetFooter()).WillByDefault(Return(new nux::VLayout())); - EXPECT_CALL(*preview.GetPointer(), GetFooter()).Times(1); - - preview->SetupViews(); -} - -} // previews - -} // dash - -} // unity diff --git a/tests/test_previews_social.cpp b/tests/test_previews_social.cpp deleted file mode 100644 index 76b42e034..000000000 --- a/tests/test_previews_social.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// -*- 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: Ken VanDine <ken.vandine@canonical.com> - */ - -#include <list> -#include <gmock/gmock.h> -using namespace testing; - -#include <Nux/Nux.h> -#include <Nux/BaseWindow.h> -#include <unity-shared/StaticCairoText.h> -#include <unity-shared/DashStyle.h> -#include <unity-shared/PreviewStyle.h> -#include <unity-shared/ThumbnailGenerator.h> - -#include <unity-protocol.h> -#include "UnityCore/SocialPreview.h" -#include "dash/previews/SocialPreview.h" -#include "dash/previews/SocialPreviewContent.h" -#include "dash/previews/SocialPreviewComments.h" -#include "dash/previews/PreviewInfoHintWidget.h" -#include "dash/previews/PreviewRatingsWidget.h" -#include "test_utils.h" -using namespace unity; -using namespace unity::dash; - -namespace -{ - -class MockSocialPreview : public previews::SocialPreview -{ -public: - typedef nux::ObjectPtr<MockSocialPreview> Ptr; - - MockSocialPreview(dash::Preview::Ptr preview_model) - : SocialPreview(preview_model) - {} - - using SocialPreview::title_; - using SocialPreview::subtitle_; - using SocialPreview::content_; - using SocialPreview::action_buttons_; - using SocialPreview::preview_info_hints_; -}; - -class TestPreviewSocial : public Test -{ -public: - TestPreviewSocial() - : parent_window_(new nux::BaseWindow("TestPreviewSocial")) - { - glib::Object<UnityProtocolPreview> proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_social_preview_new())); - - unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); - unity_protocol_preview_set_title(proto_obj, "Social Title & special char"); - unity_protocol_preview_set_subtitle(proto_obj, "Social Subtitle > special char"); - unity_protocol_preview_set_description(proto_obj, "Social Desctiption < special char"); - unity_protocol_preview_add_action(proto_obj, "action1", "Action 1", NULL, 0); - unity_protocol_preview_add_action(proto_obj, "action2", "Action 2", NULL, 0); - unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("s", "string hint 1")); - unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint 2")); - unity_protocol_preview_add_info_hint(proto_obj, "hint3", "Hint 3", NULL, g_variant_new("i", 12)); - - glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); - preview_model_ = dash::Preview::PreviewForVariant(v); - } - - nux::ObjectPtr<nux::BaseWindow> parent_window_; - dash::Preview::Ptr preview_model_; - - previews::Style panel_style; - dash::Style dash_style; - ThumbnailGenerator thumbnail_generator; -}; - -TEST_F(TestPreviewSocial, TestCreate) -{ - previews::Preview::Ptr preview_view = previews::Preview::PreviewForModel(preview_model_); - - EXPECT_TRUE(dynamic_cast<previews::SocialPreview*>(preview_view.GetPointer()) != NULL); -} - -TEST_F(TestPreviewSocial, TestUIValues) -{ - MockSocialPreview::Ptr preview_view(new MockSocialPreview(preview_model_)); - - EXPECT_EQ(preview_view->title_->GetText(), "Social Title & special char"); - EXPECT_EQ(preview_view->subtitle_->GetText(), "Social Subtitle > special char"); - EXPECT_EQ(preview_view->action_buttons_.size(), 2u); -} - -} diff --git a/unity-shared/BackgroundEffectHelper.cpp b/unity-shared/BackgroundEffectHelper.cpp index 507c3eacc..274f184c2 100644 --- a/unity-shared/BackgroundEffectHelper.cpp +++ b/unity-shared/BackgroundEffectHelper.cpp @@ -27,7 +27,7 @@ namespace DECLARE_LOGGER(logger, "unity.background_effect_helper"); const int BLUR_RADIUS = 3; -const float sigma_high = 5.0f; +const float sigma_high = 10.0f; const float sigma_med = 3.0f; const float sigma_low = 1.0f; } diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt index eddc80766..673c7ddb3 100644 --- a/unity-shared/CMakeLists.txt +++ b/unity-shared/CMakeLists.txt @@ -30,9 +30,9 @@ set (UNITY_SHARED_SOURCES DesktopApplicationManager.cpp EMConverter.cpp ExpanderView.cpp - FileManager.cpp GnomeFileManager.cpp FontSettings.cpp + FileManager.cpp GraphicsUtils.cpp IMTextEntry.cpp IconLoader.cpp diff --git a/unity-shared/DashStyle.cpp b/unity-shared/DashStyle.cpp index 66ed48c5b..b0c3eac45 100755 --- a/unity-shared/DashStyle.cpp +++ b/unity-shared/DashStyle.cpp @@ -374,35 +374,8 @@ Style::Impl::~Impl() void Style::Impl::Refresh() { - const char* const SAMPLE_MAX_TEXT = "Chromium Web Browser"; - - nux::CairoGraphics util_cg(CAIRO_FORMAT_ARGB32, 1, 1); - cairo_t* cr = util_cg.GetInternalContext(); - - auto const& font = theme::Settings::Get()->font(); - PangoFontDescription* desc = ::pango_font_description_from_string(font.c_str()); - ::pango_font_description_set_weight(desc, PANGO_WEIGHT_NORMAL); - ::pango_font_description_set_size(desc, 9 * PANGO_SCALE); - - glib::Object<PangoLayout> layout(::pango_cairo_create_layout(cr)); - ::pango_layout_set_font_description(layout, desc); - ::pango_layout_set_text(layout, SAMPLE_MAX_TEXT, -1); - - PangoContext* cxt = ::pango_layout_get_context(layout); - - GdkScreen* screen = ::gdk_screen_get_default(); - ::pango_cairo_context_set_font_options(cxt, ::gdk_screen_get_font_options(screen)); - ::pango_cairo_context_set_resolution(cxt, 96.0 * Settings::Instance().font_scaling()); - ::pango_layout_context_changed(layout); - - PangoRectangle log_rect; - ::pango_layout_get_pixel_extents(layout, NULL, &log_rect); - text_width_ = log_rect.width; - text_height_ = log_rect.height; - - owner_->changed.emit(); - - pango_font_description_free(desc); + text_width_ = 56; + text_height_ = 12; } void Style::Impl::UpdateFormFactor(FormFactor form_factor) @@ -2186,6 +2159,11 @@ BaseTexturePtr Style::GetDashRightCornerMask(double scale) const return pimpl->LoadScaledTexture("dash_top_right_corner_mask", scale); } +BaseTexturePtr Style::GetEmpty(double scale) const +{ + return pimpl->LoadScaledTexture("empty", scale); +} + BaseTexturePtr Style::GetSearchMagnifyIcon(double scale) const { return pimpl->LoadScaledTexture("search_magnify", scale); @@ -2236,33 +2214,33 @@ nux::Color const& Style::GetTextColor() const RawPixel Style::GetTileGIconSize() const { - return 64; + return 48; } RawPixel Style::GetTileImageSize() const { - return 96; + return 48; } RawPixel Style::GetTileWidth() const { - return std::max(pimpl->text_width_, 150); + return std::max(pimpl->text_width_, 106); } RawPixel Style::GetTileHeight() const { return std::max(GetTileImageSize() + (pimpl->text_height_ * 2) + 15, - GetTileImageSize() + 32); // magic design numbers. + GetTileImageSize() + 80); // magic design numbers. } RawPixel Style::GetTileIconHightlightHeight() const { - return 106; + return 48; } RawPixel Style::GetTileIconHightlightWidth() const { - return 106; + return 48; } RawPixel Style::GetHomeTileIconSize() const @@ -2363,7 +2341,7 @@ RawPixel Style::GetHSeparatorSize() const RawPixel Style::GetFilterBarWidth() const { - return 300; + return 250; } RawPixel Style::GetFilterBarLeftPadding() const @@ -2418,7 +2396,7 @@ RawPixel Style::GetFilterHighlightPadding() const RawPixel Style::GetSpaceBetweenFilterWidgets() const { - return 12; + return 6; } RawPixel Style::GetAllButtonHeight() const @@ -2498,12 +2476,12 @@ RawPixel Style::GetPlacesGroupTopSpace() const RawPixel Style::GetPlacesGroupResultTopPadding() const { - return 2; + return 19; } RawPixel Style::GetPlacesGroupResultLeftPadding() const { - return 25; + return 19; } RawPixel Style::GetCategoryHeaderLeftPadding() const diff --git a/unity-shared/DashStyle.h b/unity-shared/DashStyle.h index f080297a9..dd127c421 100755 --- a/unity-shared/DashStyle.h +++ b/unity-shared/DashStyle.h @@ -178,6 +178,8 @@ public: BaseTexturePtr GetDashLeftTile(double scale) const; BaseTexturePtr GetDashTopTile(double scale) const; + BaseTexturePtr GetEmpty(double scale) const; + BaseTexturePtr GetDashCorner(double scale) const; BaseTexturePtr GetDashCornerMask(double scale) const; BaseTexturePtr GetDashLeftCorner(double scale) const; diff --git a/unity-shared/FileManager.cpp b/unity-shared/FileManager.cpp index c48a466bb..6fc574e01 100644 --- a/unity-shared/FileManager.cpp +++ b/unity-shared/FileManager.cpp @@ -24,7 +24,7 @@ #include "GnomeFileManager.h" #include "NemoFileManager.h" -#include <gio/gdesktopappinfo.h> +#include <gio/gio.h> namespace unity { diff --git a/unity-shared/FileManager.h b/unity-shared/FileManager.h index bb636d411..6b802c677 100644 --- a/unity-shared/FileManager.h +++ b/unity-shared/FileManager.h @@ -61,4 +61,4 @@ private: } // namespace unity -#endif +#endif \ No newline at end of file diff --git a/unity-shared/GnomeFileManager.h b/unity-shared/GnomeFileManager.h index b9c3dd80f..6fa53a69e 100644 --- a/unity-shared/GnomeFileManager.h +++ b/unity-shared/GnomeFileManager.h @@ -51,4 +51,4 @@ private: } -#endif +#endif \ No newline at end of file diff --git a/unity-shared/NemoFileManager.cpp b/unity-shared/NemoFileManager.cpp index 81432cf42..e8889e6a0 100644 --- a/unity-shared/NemoFileManager.cpp +++ b/unity-shared/NemoFileManager.cpp @@ -19,12 +19,12 @@ #include "NemoFileManager.h" #include <NuxCore/Logger.h> - +#include <UnityCore/DesktopUtilities.h> #include <UnityCore/GLibDBusProxy.h> #include <UnityCore/GLibWrapper.h> -#include <gio/gdesktopappinfo.h> #include <gdk/gdk.h> #include <gio/gio.h> +#include <gio/gdesktopappinfo.h> namespace unity { @@ -38,30 +38,48 @@ const std::string FILE_SCHEMA = "file://"; const std::string NEMO_DESKTOP_ID = "nemo.desktop"; const std::string NEMO_NAME = "org.Nemo"; -const std::string NEMO_PATH = "/org/Nemo"; +const std::string NEMO_FILE_OPS_PATH = "/org/Nemo"; } struct NemoFileManager::Impl { Impl(NemoFileManager* parent) : parent_(parent) - , app_info_(g_desktop_app_info_new(NEMO_DESKTOP_ID.c_str())) { } glib::DBusProxy::Ptr NemoOperationsProxy() const { auto flags = static_cast<GDBusProxyFlags>(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES|G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS); - return std::make_shared<glib::DBusProxy>(NEMO_NAME, NEMO_PATH, + return std::make_shared<glib::DBusProxy>(NEMO_NAME, NEMO_FILE_OPS_PATH, "org.Nemo.FileOperations", G_BUS_TYPE_SESSION, flags); } - void Activate(uint64_t timestamp) - { - if (!app_info_) - return; + NemoFileManager* parent_; +}; + + +FileManager::Ptr NemoFileManager::Get() +{ + static FileManager::Ptr instance(new NemoFileManager()); + return instance; +} + +NemoFileManager::NemoFileManager() + : impl_(new Impl(this)) +{} + +NemoFileManager::~NemoFileManager() +{} +void Activate(uint64_t timestamp) +{ + glib::Cancellable cancellable; + glib::Object<GAppInfo> app_info(G_APP_INFO (g_desktop_app_info_new(NEMO_DESKTOP_ID.c_str()))); + + if (app_info) + { GdkDisplay* display = gdk_display_get_default(); glib::Object<GdkAppLaunchContext> context(gdk_display_get_app_launch_context(display)); @@ -69,11 +87,10 @@ struct NemoFileManager::Impl gdk_app_launch_context_set_timestamp(context, timestamp); auto const& gcontext = glib::object_cast<GAppLaunchContext>(context); - auto proxy = std::make_shared<glib::DBusProxy>(NEMO_NAME, NEMO_PATH, - "org.freedesktop.Application"); + auto proxy = std::make_shared<glib::DBusProxy>(NEMO_NAME, NEMO_FILE_OPS_PATH, + "org.freedesktop.Application"); - glib::String context_string(g_app_launch_context_get_startup_notify_id( - gcontext, glib::object_cast<GAppInfo>(app_info_), nullptr)); + glib::String context_string(g_app_launch_context_get_startup_notify_id(gcontext, app_info, nullptr)); if (context_string && g_utf8_validate(context_string, -1, nullptr)) { @@ -86,25 +103,8 @@ struct NemoFileManager::Impl proxy->CallBegin("Activate", param, [proxy] (GVariant*, glib::Error const&) {}); } } - - glib::Object<GDesktopAppInfo> app_info_; - NemoFileManager* parent_; -}; - - -FileManager::Ptr NemoFileManager::Get() -{ - static FileManager::Ptr instance(new NemoFileManager()); - return instance; } -NemoFileManager::NemoFileManager() - : impl_(new Impl(this)) -{} - -NemoFileManager::~NemoFileManager() -{} - void NemoFileManager::Open(std::string const& uri, uint64_t timestamp) { if (uri.empty()) @@ -152,7 +152,7 @@ void NemoFileManager::EmptyTrash(uint64_t timestamp, Window parent_xid) auto const& proxy = impl_->NemoOperationsProxy(); // Passing the proxy to the lambda we ensure that it will be destroyed when needed - impl_->Activate(timestamp); + Activate(timestamp); proxy->CallBegin("EmptyTrash", nullptr, [proxy] (GVariant*, glib::Error const&) {}); } @@ -184,7 +184,7 @@ void NemoFileManager::CopyFiles(std::set<std::string> const& uris, std::string c // Passing the proxy to the lambda we ensure that it will be destroyed when needed auto const& proxy = impl_->NemoOperationsProxy(); proxy->CallBegin("CopyURIs", parameters, [proxy] (GVariant*, glib::Error const&) {}); - impl_->Activate(timestamp); + Activate(timestamp); } } @@ -199,4 +199,4 @@ std::string NemoFileManager::LocationForWindow(ApplicationWindowPtr const& win) return std::string(); } -} // namespace unity +} // namespace unity \ No newline at end of file diff --git a/unity-shared/NemoFileManager.h b/unity-shared/NemoFileManager.h index aaa4e91d9..94321a93f 100644 --- a/unity-shared/NemoFileManager.h +++ b/unity-shared/NemoFileManager.h @@ -47,4 +47,4 @@ private: std::unique_ptr<Impl> impl_; }; -} // namespace unity +} diff --git a/unity-shared/OverlayRenderer.cpp b/unity-shared/OverlayRenderer.cpp index 475ad5e9c..e35a3e8e0 100644 --- a/unity-shared/OverlayRenderer.cpp +++ b/unity-shared/OverlayRenderer.cpp @@ -99,6 +99,8 @@ public: nux::ObjectPtr<nux::BaseTexture> right_corner_; nux::ObjectPtr<nux::BaseTexture> right_corner_mask_; + OverlayPosition dash_position = OverlayPosition::LEFT; + // temporary variable that stores the number of backgrounds we have rendered int bgs; bool visible; @@ -140,20 +142,37 @@ void OverlayRendererImpl::LoadScaledTextures() double scale = parent->scale; auto& style = dash::Style::Instance(); - horizontal_texture_ = style.GetDashHorizontalTile(scale); - horizontal_texture_mask_ = style.GetDashHorizontalTileMask(scale); - right_texture_ = style.GetDashRightTile(scale); - right_texture_mask_ = style.GetDashRightTileMask(scale); - top_left_texture_ = style.GetDashTopLeftTile(scale); - left_texture_ = style.GetDashLeftTile(scale); - top_texture_ = style.GetDashTopTile(scale); - - corner_ = style.GetDashCorner(scale); - corner_mask_ = style.GetDashCornerMask(scale); - left_corner_ = style.GetDashLeftCorner(scale); - left_corner_mask_ = style.GetDashLeftCornerMask(scale); - right_corner_ = style.GetDashRightCorner(scale); - right_corner_mask_ = style.GetDashRightCornerMask(scale); + if (parent->owner_type == OverlayOwner::Hud) { + horizontal_texture_ = style.GetDashHorizontalTile(scale); + horizontal_texture_mask_ = style.GetDashHorizontalTileMask(scale); + right_texture_ = style.GetDashRightTile(scale); + right_texture_mask_ = style.GetDashRightTileMask(scale); + top_left_texture_ = style.GetDashTopLeftTile(scale); + left_texture_ = style.GetDashLeftTile(scale); + top_texture_ = style.GetDashTopTile(scale); + + corner_ = style.GetDashCorner(scale); + corner_mask_ = style.GetDashCornerMask(scale); + left_corner_ = style.GetDashLeftCorner(scale); + left_corner_mask_ = style.GetDashLeftCornerMask(scale); + right_corner_ = style.GetDashRightCorner(scale); + right_corner_mask_ = style.GetDashRightCornerMask(scale); + } else { + horizontal_texture_ = style.GetEmpty(scale); + horizontal_texture_mask_ = style.GetEmpty(scale); + right_texture_ = style.GetEmpty(scale); + right_texture_mask_ = style.GetEmpty(scale); + top_left_texture_ = style.GetEmpty(scale); + left_texture_ = style.GetEmpty(scale); + top_texture_ = style.GetEmpty(scale); + + corner_ = style.GetEmpty(scale); + corner_mask_ = style.GetEmpty(scale); + left_corner_ = style.GetEmpty(scale); + left_corner_mask_ = style.GetEmpty(scale); + right_corner_ = style.GetEmpty(scale); + right_corner_mask_ = style.GetEmpty(scale); + } } void OverlayRendererImpl::OnBgColorChanged(nux::Color const& new_color) @@ -186,7 +205,7 @@ void OverlayRendererImpl::UpdateTextures() rop.Blend = true; rop.SrcBlend = GL_ZERO; rop.DstBlend = GL_SRC_COLOR; - nux::Color darken_colour = nux::Color(0.9f, 0.9f, 0.9f, 1.0f); + nux::Color darken_colour = nux::Color(1.0f, 1.0f, 1.0f, 1.0f); //When we are in low gfx mode then our darken layer will act as a background. if (Settings::Instance().low_gfx()) @@ -198,7 +217,7 @@ void OverlayRendererImpl::UpdateTextures() } bg_darken_layer_ = std::make_shared<nux::ColorLayer>(darken_colour, false, rop); - bg_shine_texture_ = dash::Style::Instance().GetDashShine()->GetDeviceTexture(); + // bg_shine_texture_ = dash::Style::Instance().GetDashShine()->GetDeviceTexture(); auto const& bg_refine_tex = dash::Style::Instance().GetRefineTextureDash(); @@ -619,7 +638,6 @@ void OverlayRendererImpl::Draw(nux::GraphicsEngine& gfx_context, nux::Geometry c int launcher_size = Settings::Instance().LauncherSize(monitor); int panel_height = panel::Style::Instance().PanelHeight(monitor); - auto dash_position = OverlayPosition::LEFT; int border_y = content_geo.y; int border_height = larger_absolute_geo.height; if (parent->owner_type() == OverlayOwner::Dash && settings.launcher_position() == LauncherPosition::BOTTOM) diff --git a/unity-shared/OverlayWindowButtons.cpp b/unity-shared/OverlayWindowButtons.cpp index c373ad191..6b17182ee 100644 --- a/unity-shared/OverlayWindowButtons.cpp +++ b/unity-shared/OverlayWindowButtons.cpp @@ -23,7 +23,7 @@ namespace { - const int MAIN_LEFT_PADDING = 4; + const int MAIN_LEFT_PADDING = 6; const int MENUBAR_PADDING = 4; } diff --git a/unity-shared/PanelStyle.cpp b/unity-shared/PanelStyle.cpp index e2fb22d2e..92d675164 100644 --- a/unity-shared/PanelStyle.cpp +++ b/unity-shared/PanelStyle.cpp @@ -41,8 +41,8 @@ Style* style_instance = nullptr; DECLARE_LOGGER(logger, "unity.panel.style"); const int BUTTONS_SIZE = 16; -const int BUTTONS_PADDING = 1; -const int BASE_PANEL_HEIGHT = 24; +const int BUTTONS_PADDING = 9; +const int BASE_PANEL_HEIGHT = 30; const std::string PANEL_STYLE_CSS_NAME = "UnityPanelWidget"; inline std::string button_id(std::string const& prefix, double scale, WindowButtonType type, WindowState ws) diff --git a/unity-shared/SearchBar.cpp b/unity-shared/SearchBar.cpp index 9c3741f86..8e90c58a5 100644 --- a/unity-shared/SearchBar.cpp +++ b/unity-shared/SearchBar.cpp @@ -74,11 +74,11 @@ const RawPixel FILTER_HORIZONTAL_MARGIN = 8_em; // Fonts const std::string HINT_LABEL_FONT_SIZE = "15"; // == 20px -const std::string HINT_LABEL_FONT_STYLE = "Italic"; +const std::string HINT_LABEL_FONT_STYLE = "Light"; const std::string HINT_LABEL_DEFAULT_FONT = "Ubuntu " + HINT_LABEL_FONT_STYLE + " " + HINT_LABEL_FONT_SIZE; -const std::string PANGO_ENTRY_DEFAULT_FONT_FAMILY = "Ubuntu"; -const RawPixel PANGO_ENTRY_FONT_SIZE = 22_em; +const std::string PANGO_ENTRY_DEFAULT_FONT_FAMILY = "Ubuntu Light"; +const RawPixel PANGO_ENTRY_FONT_SIZE = 15_em; const std::string SHOW_FILTERS_LABEL_FONT_SIZE = "13"; const std::string SHOW_FILTERS_LABEL_FONT_STYLE = ""; @@ -403,8 +403,6 @@ void SearchBar::Draw(nux::GraphicsEngine& graphics_engine, bool force_draw) { nux::Geometry const& base = GetGeometry(); - UpdateBackground(false); - graphics_engine.PushClippingRectangle(base); if (RedirectedAncestor()) @@ -513,64 +511,6 @@ void SearchBar::SetSearchFinished() spinner_->SetState(is_empty ? STATE_READY : STATE_CLEAR); } -void SearchBar::UpdateBackground(bool force) -{ - nux::Geometry geo(GetGeometry()); - geo.width = layered_layout_->GetAbsoluteX() + - layered_layout_->GetAbsoluteWidth() - - GetAbsoluteX() + - SEARCH_ENTRY_RIGHT_BORDER.CP(scale()); - - LOG_TRACE(logger) << "height: " - << geo.height << " - " - << layered_layout_->GetGeometry().height << " - " - << pango_entry_->GetGeometry().height; - - if (!bg_layer_ && - geo.width == last_width_ - && geo.height == last_height_ - && force == false) - return; - - last_width_ = geo.width; - last_height_ = geo.height; - - nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, last_width_, last_height_); - cairo_t* cr = cairo_graphics.GetInternalContext(); - cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); - - cairo_graphics.DrawRoundedRectangle(cr, - 1.0f, - 0.5, 0.5, - CORNER_RADIUS, - (last_width_/scale) - 1, (last_height_/scale) - 1, - false); - - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.35f); - cairo_fill_preserve(cr); - cairo_set_line_width(cr, 1); - cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 0.7f); - cairo_stroke(cr); - - auto texture2D = texture_ptr_from_cairo_graphics(cairo_graphics); - - nux::TexCoordXForm texxform; - texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); - texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); - - nux::ROPConfig rop; - rop.Blend = true; - rop.SrcBlend = GL_ONE; - rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; - - bg_layer_.reset(new nux::TextureLayer(texture2D->GetDeviceTexture(), - texxform, - nux::color::White, - true, - rop)); -} - void SearchBar::OnMouseButtonDown(int x, int y, unsigned long button, unsigned long key) { hint_->SetVisible(false); diff --git a/unity-shared/SearchBar.h b/unity-shared/SearchBar.h index c9af1db11..fa2833272 100644 --- a/unity-shared/SearchBar.h +++ b/unity-shared/SearchBar.h @@ -82,7 +82,6 @@ private: void OnMouseButtonDown(int x, int y, unsigned long button_flags, unsigned long key_flags); void OnEndKeyFocus(); - void UpdateBackground(bool force); void OnSearchChanged(nux::TextEntry* text_entry); void OnClearClicked(int x, int y, unsigned long button_flags, unsigned long key_flags); void OnEntryActivated(); diff --git a/unity-standalone/StandaloneUnity.cpp b/unity-standalone/StandaloneUnity.cpp index 6a0af61b6..29ec25c9a 100644 --- a/unity-standalone/StandaloneUnity.cpp +++ b/unity-standalone/StandaloneUnity.cpp @@ -157,6 +157,8 @@ int main(int argc, char **argv) GError *error = NULL; GOptionContext *context; + unity::Settings settings; + nux::NuxInitialize(0); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); @@ -177,7 +179,6 @@ int main(int argc, char **argv) FontSettings font_settings; // The instances for the pseudo-singletons. - Settings settings; settings.is_standalone = true; if (force_tv) Settings::Instance().form_factor(FormFactor::TV); diff --git a/uwidgets/LICENCE b/uwidgets/LICENCE new file mode 100644 index 000000000..32b38d425 --- /dev/null +++ b/uwidgets/LICENCE @@ -0,0 +1,636 @@ +# GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 [Free Software Foundation, Inc.](http://fsf.org/) + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +## Preamble + +The GNU General Public License is a free, copyleft license for software and +other kinds of works. + +The licenses for most software and other practical works are designed to take +away your freedom to share and change the works. By contrast, the GNU General +Public License is intended to guarantee your freedom to share and change all +versions of a program--to make sure it remains free software for all its users. +We, the Free Software Foundation, use the GNU General Public License for most +of our software; it applies also to any other work released this way by its +authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for them if you wish), that you +receive source code or can get it if you want it, that you can change the +software or use pieces of it in new free programs, and that you know you can do +these things. + +To protect your rights, we need to prevent others from denying you these rights +or asking you to surrender the rights. Therefore, you have certain +responsibilities if you distribute copies of the software, or if you modify it: +responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must pass on to the recipients the same freedoms that you received. +You must make sure that they, too, receive or can get the source code. And you +must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: + + 1. assert copyright on the software, and + 2. offer you this License giving you legal permission to copy, distribute + and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that +there is no warranty for this free software. For both users' and authors' sake, +the GPL requires that modified versions be marked as changed, so that their +problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified +versions of the software inside them, although the manufacturer can do so. This +is fundamentally incompatible with the aim of protecting users' freedom to +change the software. The systematic pattern of such abuse occurs in the area of +products for individuals to use, which is precisely where it is most +unacceptable. Therefore, we have designed this version of the GPL to prohibit +the practice for those products. If such problems arise substantially in other +domains, we stand ready to extend this provision to those domains in future +versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States +should not allow patents to restrict development and use of software on +general-purpose computers, but in those that do, we wish to avoid the special +danger that patents applied to a free program could make it effectively +proprietary. To prevent this, the GPL assures that patents cannot be used to +render the program non-free. + +The precise terms and conditions for copying, distribution and modification +follow. + +## TERMS AND CONDITIONS + +### 0. Definitions. + +*This License* refers to version 3 of the GNU General Public License. + +*Copyright* also means copyright-like laws that apply to other kinds of works, +such as semiconductor masks. + +*The Program* refers to any copyrightable work licensed under this License. +Each licensee is addressed as *you*. *Licensees* and *recipients* may be +individuals or organizations. + +To *modify* a work means to copy from or adapt all or part of the work in a +fashion requiring copyright permission, other than the making of an exact copy. +The resulting work is called a *modified version* of the earlier work or a work +*based on* the earlier work. + +A *covered work* means either the unmodified Program or a work based on the +Program. + +To *propagate* a work means to do anything with it that, without permission, +would make you directly or secondarily liable for infringement under applicable +copyright law, except executing it on a computer or modifying a private copy. +Propagation includes copying, distribution (with or without modification), +making available to the public, and in some countries other activities as well. + +To *convey* a work means any kind of propagation that enables other parties to +make or receive copies. Mere interaction with a user through a computer +network, with no transfer of a copy, is not conveying. + +An interactive user interface displays *Appropriate Legal Notices* to the +extent that it includes a convenient and prominently visible feature that + + 1. displays an appropriate copyright notice, and + 2. tells the user that there is no warranty for the work (except to the + extent that warranties are provided), that licensees may convey the work + under this License, and how to view a copy of this License. + +If the interface presents a list of user commands or options, such as a menu, a +prominent item in the list meets this criterion. + +### 1. Source Code. + +The *source code* for a work means the preferred form of the work for making +modifications to it. *Object code* means any non-source form of a work. + +A *Standard Interface* means an interface that either is an official standard +defined by a recognized standards body, or, in the case of interfaces specified +for a particular programming language, one that is widely used among developers +working in that language. + +The *System Libraries* of an executable work include anything, other than the +work as a whole, that (a) is included in the normal form of packaging a Major +Component, but which is not part of that Major Component, and (b) serves only +to enable use of the work with that Major Component, or to implement a Standard +Interface for which an implementation is available to the public in source code +form. A *Major Component*, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system (if any) on +which the executable work runs, or a compiler used to produce the work, or an +object code interpreter used to run it. + +The *Corresponding Source* for a work in object code form means all the source +code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. +However, it does not include the work's System Libraries, or general-purpose +tools or generally available free programs which are used unmodified in +performing those activities but which are not part of the work. For example, +Corresponding Source includes interface definition files associated with source +files for the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, such as +by intimate data communication or control flow between those subprograms and +other parts of the work. + +The Corresponding Source need not include anything that users can regenerate +automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +### 2. Basic Permissions. + +All rights granted under this License are granted for the term of copyright on +the Program, and are irrevocable provided the stated conditions are met. This +License explicitly affirms your unlimited permission to run the unmodified +Program. The output from running a covered work is covered by this License only +if the output, given its content, constitutes a covered work. This License +acknowledges your rights of fair use or other equivalent, as provided by +copyright law. + +You may make, run and propagate covered works that you do not convey, without +conditions so long as your license otherwise remains in force. You may convey +covered works to others for the sole purpose of having them make modifications +exclusively for you, or provide you with facilities for running those works, +provided that you comply with the terms of this License in conveying all +material for which you do not control copyright. Those thus making or running +the covered works for you must do so exclusively on your behalf, under your +direction and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes it +unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological measure +under any applicable law fulfilling obligations under article 11 of the WIPO +copyright treaty adopted on 20 December 1996, or similar laws prohibiting or +restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention is +effected by exercising rights under this License with respect to the covered +work, and you disclaim any intention to limit operation or modification of the +work as a means of enforcing, against the work's users, your or third parties' +legal rights to forbid circumvention of technological measures. + +### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on +each copy an appropriate copyright notice; keep intact all notices stating that +this License and any non-permissive terms added in accord with section 7 apply +to the code; keep intact all notices of the absence of any warranty; and give +all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may +offer support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to produce it +from the Program, in the form of source code under the terms of section 4, +provided that you also meet all of these conditions: + + - a) The work must carry prominent notices stating that you modified it, and + giving a relevant date. + - b) The work must carry prominent notices stating that it is released under + this License and any conditions added under section 7. This requirement + modifies the requirement in section 4 to *keep intact all notices*. + - c) You must license the entire work, as a whole, under this License to + anyone who comes into possession of a copy. This License will therefore + apply, along with any applicable section 7 additional terms, to the whole + of the work, and all its parts, regardless of how they are packaged. This + License gives no permission to license the work in any other way, but it + does not invalidate such permission if you have separately received it. + - d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your work need + not make them do so. + +A compilation of a covered work with other separate and independent works, +which are not by their nature extensions of the covered work, and which are not +combined with it such as to form a larger program, in or on a volume of a +storage or distribution medium, is called an *aggregate* if the compilation and +its resulting copyright are not used to limit the access or legal rights of the +compilation's users beyond what the individual works permit. Inclusion of a +covered work in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of sections 4 +and 5, provided that you also convey the machine-readable Corresponding Source +under the terms of this License, in one of these ways: + + - a) Convey the object code in, or embodied in, a physical product (including + a physical distribution medium), accompanied by the Corresponding Source + fixed on a durable physical medium customarily used for software + interchange. + - b) Convey the object code in, or embodied in, a physical product (including + a physical distribution medium), accompanied by a written offer, valid for + at least three years and valid for as long as you offer spare parts or + customer support for that product model, to give anyone who possesses the + object code either + 1. a copy of the Corresponding Source for all the software in the product + that is covered by this License, on a durable physical medium + customarily used for software interchange, for a price no more than your + reasonable cost of physically performing this conveying of source, or + 2. access to copy the Corresponding Source from a network server at no + charge. + - c) Convey individual copies of the object code with a copy of the written + offer to provide the Corresponding Source. This alternative is allowed only + occasionally and noncommercially, and only if you received the object code + with such an offer, in accord with subsection 6b. + - d) Convey the object code by offering access from a designated place + (gratis or for a charge), and offer equivalent access to the Corresponding + Source in the same way through the same place at no further charge. You + need not require recipients to copy the Corresponding Source along with the + object code. If the place to copy the object code is a network server, the + Corresponding Source may be on a different server operated by you or a + third party) that supports equivalent copying facilities, provided you + maintain clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the Corresponding + Source, you remain obligated to ensure that it is available for as long as + needed to satisfy these requirements. + - e) Convey the object code using peer-to-peer transmission, provided you + inform other peers where the object code and Corresponding Source of the + work are being offered to the general public at no charge under subsection + 6d. + +A separable portion of the object code, whose source code is excluded from the +Corresponding Source as a System Library, need not be included in conveying the +object code work. + +A *User Product* is either + + 1. a *consumer product*, which means any tangible personal property which is + normally used for personal, family, or household purposes, or + 2. anything designed or sold for incorporation into a dwelling. + +In determining whether a product is a consumer product, doubtful cases shall be +resolved in favor of coverage. For a particular product received by a +particular user, *normally used* refers to a typical or common use of that +class of product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected to use, +the product. A product is a consumer product regardless of whether the product +has substantial commercial, industrial or non-consumer uses, unless such uses +represent the only significant mode of use of the product. + +*Installation Information* for a User Product means any methods, procedures, +authorization keys, or other information required to install and execute +modified versions of a covered work in that User Product from a modified +version of its Corresponding Source. The information must suffice to ensure +that the continued functioning of the modified object code is in no case +prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as part of a +transaction in which the right of possession and use of the User Product is +transferred to the recipient in perpetuity or for a fixed term (regardless of +how the transaction is characterized), the Corresponding Source conveyed under +this section must be accompanied by the Installation Information. But this +requirement does not apply if neither you nor any third party retains the +ability to install modified object code on the User Product (for example, the +work has been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates for a +work that has been modified or installed by the recipient, or for the User +Product in which it has been modified or installed. Access to a network may be +denied when the modification itself materially and adversely affects the +operation of the network or violates the rules and protocols for communication +across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord +with this section must be in a format that is publicly documented (and with an +implementation available to the public in source code form), and must require +no special password or key for unpacking, reading or copying. + +### 7. Additional Terms. + +*Additional permissions* are terms that supplement the terms of this License by +making exceptions from one or more of its conditions. Additional permissions +that are applicable to the entire Program shall be treated as though they were +included in this License, to the extent that they are valid under applicable +law. If additional permissions apply only to part of the Program, that part may +be used separately under those permissions, but the entire Program remains +governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any +additional permissions from that copy, or from any part of it. (Additional +permissions may be written to require their own removal in certain cases when +you modify the work.) You may place additional permissions on material, added +by you to a covered work, for which you have or can give appropriate copyright +permission. + +Notwithstanding any other provision of this License, for material you add to a +covered work, you may (if authorized by the copyright holders of that material) +supplement the terms of this License with terms: + + - a) Disclaiming warranty or limiting liability differently from the terms of + sections 15 and 16 of this License; or + - b) Requiring preservation of specified reasonable legal notices or author + attributions in that material or in the Appropriate Legal Notices displayed + by works containing it; or + - c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in reasonable + ways as different from the original version; or + - d) Limiting the use for publicity purposes of names of licensors or authors + of the material; or + - e) Declining to grant rights under trademark law for use of some trade + names, trademarks, or service marks; or + - f) Requiring indemnification of licensors and authors of that material by + anyone who conveys the material (or modified versions of it) with + contractual assumptions of liability to the recipient, for any liability + that these contractual assumptions directly impose on those licensors and + authors. + +All other non-permissive additional terms are considered *further restrictions* +within the meaning of section 10. If the Program as you received it, or any +part of it, contains a notice stating that it is governed by this License along +with a term that is a further restriction, you may remove that term. If a +license document contains a further restriction but permits relicensing or +conveying under this License, you may add to a covered work material governed +by the terms of that license document, provided that the further restriction +does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, +in the relevant source files, a statement of the additional terms that apply to +those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a +separately written license, or stated as exceptions; the above requirements +apply either way. + +### 8. Termination. + +You may not propagate or modify a covered work except as expressly provided +under this License. Any attempt otherwise to propagate or modify it is void, +and will automatically terminate your rights under this License (including any +patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a +particular copyright holder is reinstated + + - a) provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and + - b) permanently, if the copyright holder fails to notify you of the + violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated +permanently if the copyright holder notifies you of the violation by some +reasonable means, this is the first time you have received notice of violation +of this License (for any work) from that copyright holder, and you cure the +violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses +of parties who have received copies or rights from you under this License. If +your rights have been terminated and not permanently reinstated, you do not +qualify to receive new licenses for the same material under section 10. + +### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run a copy +of the Program. Ancillary propagation of a covered work occurring solely as a +consequence of using peer-to-peer transmission to receive a copy likewise does +not require acceptance. However, nothing other than this License grants you +permission to propagate or modify any covered work. These actions infringe +copyright if you do not accept this License. Therefore, by modifying or +propagating a covered work, you indicate your acceptance of this License to do +so. + +### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically receives a +license from the original licensors, to run, modify and propagate that work, +subject to this License. You are not responsible for enforcing compliance by +third parties with this License. + +An *entity transaction* is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered work +results from an entity transaction, each party to that transaction who receives +a copy of the work also receives whatever licenses to the work the party's +predecessor in interest had or could give under the previous paragraph, plus a +right to possession of the Corresponding Source of the work from the +predecessor in interest, if the predecessor has it or can get it with +reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights +granted or affirmed under this License. For example, you may not impose a +license fee, royalty, or other charge for exercise of rights granted under this +License, and you may not initiate litigation (including a cross-claim or +counterclaim in a lawsuit) alleging that any patent claim is infringed by +making, using, selling, offering for sale, or importing the Program or any +portion of it. + +### 11. Patents. + +A *contributor* is a copyright holder who authorizes use under this License of +the Program or a work on which the Program is based. The work thus licensed is +called the contributor's *contributor version*. + +A contributor's *essential patent claims* are all patent claims owned or +controlled by the contributor, whether already acquired or hereafter acquired, +that would be infringed by some manner, permitted by this License, of making, +using, or selling its contributor version, but do not include claims that would +be infringed only as a consequence of further modification of the contributor +version. For purposes of this definition, *control* includes the right to grant +patent sublicenses in a manner consistent with the requirements of this +License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent +license under the contributor's essential patent claims, to make, use, sell, +offer for sale, import and otherwise run, modify and propagate the contents of +its contributor version. + +In the following three paragraphs, a *patent license* is any express agreement +or commitment, however denominated, not to enforce a patent (such as an express +permission to practice a patent or covenant not to sue for patent +infringement). To *grant* such a patent license to a party means to make such +an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the +Corresponding Source of the work is not available for anyone to copy, free of +charge and under the terms of this License, through a publicly available +network server or other readily accessible means, then you must either + + 1. cause the Corresponding Source to be so available, or + 2. arrange to deprive yourself of the benefit of the patent license for this + particular work, or + 3. arrange, in a manner consistent with the requirements of this License, to + extend the patent license to downstream recipients. + +*Knowingly relying* means you have actual knowledge that, but for the patent +license, your conveying the covered work in a country, or your recipient's use +of the covered work in a country, would infringe one or more identifiable +patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you +convey, or propagate by procuring conveyance of, a covered work, and grant a +patent license to some of the parties receiving the covered work authorizing +them to use, propagate, modify or convey a specific copy of the covered work, +then the patent license you grant is automatically extended to all recipients +of the covered work and works based on it. + +A patent license is *discriminatory* if it does not include within the scope of +its coverage, prohibits the exercise of, or is conditioned on the non-exercise +of one or more of the rights that are specifically granted under this License. +You may not convey a covered work if you are a party to an arrangement with a +third party that is in the business of distributing software, under which you +make payment to the third party based on the extent of your activity of +conveying the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory patent +license + + - a) in connection with copies of the covered work conveyed by you (or copies + made from those copies), or + - b) primarily for and in connection with specific products or compilations + that contain the covered work, unless you entered into that arrangement, or + that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied +license or other defenses to infringement that may otherwise be available to +you under applicable patent law. + +### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not excuse +you from the conditions of this License. If you cannot convey a covered work so +as to satisfy simultaneously your obligations under this License and any other +pertinent obligations, then as a consequence you may not convey it at all. For +example, if you agree to terms that obligate you to collect a royalty for +further conveying from those to whom you convey the Program, the only way you +could satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have permission to +link or combine any covered work with a work licensed under version 3 of the +GNU Affero General Public License into a single combined work, and to convey +the resulting work. The terms of this License will continue to apply to the +part which is the covered work, but the special requirements of the GNU Affero +General Public License, section 13, concerning interaction through a network +will apply to the combination as such. + +### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of the GNU +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program specifies +that a certain numbered version of the GNU General Public License *or any later +version* applies to it, you have the option of following the terms and +conditions either of that numbered version or of any later version published by +the Free Software Foundation. If the Program does not specify a version number +of the GNU General Public License, you may choose any version ever published by +the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the +GNU General Public License can be used, that proxy's public statement of +acceptance of a version permanently authorizes you to choose that version for +the Program. + +Later license versions may give you additional or different permissions. +However, no additional obligations are imposed on any author or copyright +holder as a result of your choosing to follow a later version. + +### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE +LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER +PARTIES PROVIDE THE PROGRAM *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE +QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE +THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY +HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided above cannot +be given local legal effect according to their terms, reviewing courts shall +apply local law that most closely approximates an absolute waiver of all civil +liability in connection with the Program, unless a warranty or assumption of +liability accompanies a copy of the Program in return for a fee. + +## END OF TERMS AND CONDITIONS ### + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively state the exclusion +of warranty; and each file should have at least the *copyright* line and a +pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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/>. + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like +this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w` and `show c` should show the appropriate +parts of the General Public License. Of course, your program's commands might +be different; for a GUI interface, you would use an *about box*. + +You should also get your employer (if you work as a programmer) or school, if +any, to sign a *copyright disclaimer* for the program, if necessary. For more +information on this, and how to apply and follow the GNU GPL, see +[http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). + +The GNU General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may consider +it more useful to permit linking proprietary applications with the library. If +this is what you want to do, use the GNU Lesser General Public License instead +of this License. But first, please read +[http://www.gnu.org/philosophy/why-not-lgpl.html](http://www.gnu.org/philosophy/why-not-lgpl.html). diff --git a/uwidgets/MANIFEST.in b/uwidgets/MANIFEST.in new file mode 100644 index 000000000..a47d446b4 --- /dev/null +++ b/uwidgets/MANIFEST.in @@ -0,0 +1,3 @@ +include LICENSE +include README.md +include uwidgets/x11/*.h diff --git a/uwidgets/official-widgets/clock/clock.py b/uwidgets/official-widgets/clock/clock.py new file mode 100755 index 000000000..121a1065e --- /dev/null +++ b/uwidgets/official-widgets/clock/clock.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 + +""" +This file is part of "blighty" and "uwidgets" which is released under GPL. + +See file LICENCE or go to http://www.gnu.org/licenses/ for full license +details. + +uwidgets is a desktop widget creation and management library for Python 3. + +Copyright (c) 2022 Rudra Saraswat <rs2009@ubuntu.com>. +Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +All rights reserved. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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/>. +""" + +from uwidgets import CanvasGravity, brush +from uwidgets.x11 import Canvas, start_event_loop + +import datetime +import subprocess +import configparser + +from math import pi as PI + + +class Clock(Canvas): + def on_button_pressed(self, button, state, x, y): + if button == 3: # Right button + subprocess.run(['unity-control-center', 'datetime']) + + def draw_circle_background(ctx): + ctx.arc(2, 1, 90, 0, 2*PI) + ctx.set_source_rgba(0, 0, 0, 0.6) + ctx.fill() + + def draw_rect_background(ctx): + ctx.rectangle(-180, -180, 360, 360) + ctx.set_source_rgba(0, 0, 0, 0.6) + ctx.fill() + + ctx.rectangle(-180, -100, 360, 3) + ctx.set_source_rgba(1, 1, 1, 1) + ctx.fill() + + @brush + def hand(ctx, angle, length, thickness): + ctx.save() + ctx.set_source_rgba(1, 1, 1, 1) + ctx.set_line_width(thickness) + ctx.rotate(angle) + ctx.move_to(0, length * .2) + ctx.line_to(0, -length) + ctx.stroke() + ctx.restore() + + def on_draw(self, ctx): + now = datetime.datetime.now() + + ctx.translate(self.width >> 1, self.height >> 1) + + getattr(ctx, f"draw_{config.get('settings', 'clock_style')}_background")() + + ctx.hand( + angle = now.second / 30 * PI, + length = ((self.height >> 1) * .9) - 20, + thickness = 1 + ) + + mins = now.minute + now.second / 60 + ctx.hand( + angle = mins / 30 * PI, + length = ((self.height >> 1) * .8) - 20, + thickness = 3 + ) + + hours = (now.hour % 12) + mins / 60 + ctx.hand( + angle = hours / 6 * PI, + length = ((self.height >> 1) * .5) - 20, + thickness = 4.5 + ) + +if __name__ == "__main__": + config = configparser.ConfigParser() + config.read('settings.ini') + clock = Clock(int(config.get('settings', 'margin_x')), + int(config.get('settings', 'margin_y')), + 200, + 200, + gravity = getattr(CanvasGravity, str(config.get('settings', 'gravity')))) + clock.show() + start_event_loop() diff --git a/uwidgets/official-widgets/clock/settings.ini b/uwidgets/official-widgets/clock/settings.ini new file mode 100644 index 000000000..cae5b6b53 --- /dev/null +++ b/uwidgets/official-widgets/clock/settings.ini @@ -0,0 +1,7 @@ +[settings] +# supported options for gravity: NORTH_WEST, NORTH, NORTH_EAST, WEST, CENTER, EAST, SOUTH_WEST, SOUTH, SOUTH_EAST (default) +gravity=SOUTH_EAST +margin_x=360 +margin_y=150 +# supported options for gravity: rect (default), circle +clock_style=rect \ No newline at end of file diff --git a/uwidgets/official-widgets/clock/widget.ini b/uwidgets/official-widgets/clock/widget.ini new file mode 100644 index 000000000..b7d474ec1 --- /dev/null +++ b/uwidgets/official-widgets/clock/widget.ini @@ -0,0 +1,5 @@ +[widget] +name=Clock +summary=A clock widget for the Unity desktop. +exec=python3 clock.py +enabled=true \ No newline at end of file diff --git a/uwidgets/official-widgets/cpu/cpu.py b/uwidgets/official-widgets/cpu/cpu.py new file mode 100755 index 000000000..acca57f2b --- /dev/null +++ b/uwidgets/official-widgets/cpu/cpu.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 + +from math import pi as PI + +import configparser +import psutil +import cairo +import os +from uwidgets import CanvasGravity, TextAlign +from uwidgets.legacy import Graph +from uwidgets.x11 import Canvas, start_event_loop + + +class AttrDict(dict): + def __init__(self, *args, **kwargs): + super(AttrDict, self).__init__(*args, **kwargs) + self.__dict__ = self + +class Fonts: + UBUNTU_NORMAL = "Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.NORMAL + + +class Cpu(Canvas): + SIZE = (256, 256) + CORE_POLYGON = AttrDict({"height": 30, "length": 20}) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + with open('/proc/cpuinfo', 'r') as fin: + raw_cpuinfo = fin.read().strip() + + self.coreinfo = [ + { + k.strip(): v + for k, v in [p.split(":") for p in core.split('\n')] + } + for core in raw_cpuinfo.split('\n\n') + ] + + self.graph = Graph(0, 110, self.width, 40) + + @staticmethod + def build(x = 0, y = 0, gravity = CanvasGravity.CENTER): + return Cpu(x, y, *Cpu.SIZE, gravity = gravity, interval = 2000) + + def on_button_pressed(self, button, *args): + os.system('stacer') + + def draw_polygon(c, n, x, y, size): + a = 2 * PI / n + + c.save() + + c.translate(x, y) + c.move_to(size, 0) + for i in range(n): + c.rotate(a) + c.line_to(size, 0) + c.stroke() + + c.restore() + + def draw_core_polygon(c, x, y): + size = Cpu.CORE_POLYGON.height + length = Cpu.CORE_POLYGON.length + + c.save() + + c.translate(x, y) + + c.set_source_rgb(.8, .8, .8) + + cpus = psutil.cpu_percent(0.1, percpu = True) + n = len(cpus) + a = 2 * PI / n + + c.set_line_width(1) + c.draw_polygon(n, 0, 0, size) + + c.set_line_width(2) + c.set_source_rgb(1, 1, 1) + c.move_to(size + length * cpus[-1] / 100, 0) + for i in range(n): + c.rotate(a) + c.line_to(size + length * cpus[i] / 100, 0) + c.stroke() + + value = int(sum(cpus) / n) + c.canvas.graph.push_value(value) + + c.set_font_size(18) + c.write_text(0, 0, '{}%'.format(value), TextAlign.CENTER_MIDDLE) + + c.restore() + + return value + + def draw_processes(c): + ps = [ + p.info + for p in psutil.process_iter(attrs=['pid', 'name', 'cpu_percent']) + ] + + ps = sorted(ps, key=lambda p: p["cpu_percent"], reverse=True)[:5] + + y = 170 + c.save() + c.select_font_face(*Fonts.UBUNTU_NORMAL) + c.set_font_size(12) + for p in ps: + c.write_text(48, y, str(p["pid"]), align = TextAlign.TOP_RIGHT) + c.write_text(52, y, p["name"][:24]) + c.write_text( + c.canvas.width - 15, y, "{}%".format(p["cpu_percent"]), + align=TextAlign.TOP_RIGHT + ) + y += 18 + + c.restore() + + def draw_cpu_name(c): + c.save() + c.set_font_size(12) + c.write_text( + 15, 110, + c.canvas.coreinfo[0]["model name"].strip() + .replace("(TM)", "™") + .replace("(R)", "©") + ) + c.restore() + + def draw_background(c): + size = c.canvas.get_size() + c.rectangle(0, 0, *size) + c.set_source_rgba(0, 0, 0, 0.6) + c.fill() + + c.rectangle(0, 0, c.canvas.width, 3) + c.set_source_rgba(1, 1, 1, 1) + c.fill() + + def on_draw(self, c): + c.draw_background() + + c.select_font_face(*Fonts.UBUNTU_NORMAL) + c.set_font_size(36) + c.set_source_rgb(1, 1, 1) + + w, h = Cpu.SIZE + + y_poly = (Cpu.CORE_POLYGON.height + Cpu.CORE_POLYGON.length) + + c.write_text(15, y_poly, "CPU", align = TextAlign.TOP_LEFT) + c.draw_core_polygon(w - y_poly, y_poly) + c.draw_processes() + c.draw_cpu_name() + + c.set_source_rgb(1, 1, 1) + self.graph.draw(c) + + +if __name__ == "__main__": + config = configparser.ConfigParser() + config.read('settings.ini') + Cpu.build(int(config.get('settings', 'margin_x')), + int(config.get('settings', 'margin_y')), + gravity = getattr(CanvasGravity, config.get('settings', 'gravity'))).show() + start_event_loop() diff --git a/uwidgets/official-widgets/cpu/settings.ini b/uwidgets/official-widgets/cpu/settings.ini new file mode 100644 index 000000000..f3f809114 --- /dev/null +++ b/uwidgets/official-widgets/cpu/settings.ini @@ -0,0 +1,5 @@ +[settings] +# supported options for gravity: NORTH_WEST, NORTH, NORTH_EAST, WEST, CENTER, EAST, SOUTH_WEST, SOUTH, SOUTH_EAST (default) +gravity=SOUTH_EAST +margin_x=20 +margin_y=150 \ No newline at end of file diff --git a/uwidgets/official-widgets/cpu/widget.ini b/uwidgets/official-widgets/cpu/widget.ini new file mode 100644 index 000000000..49532fdc1 --- /dev/null +++ b/uwidgets/official-widgets/cpu/widget.ini @@ -0,0 +1,5 @@ +[widget] +name=CPU +summary=A system monitor widget for the Unity desktop (that links to Stacer). +exec=python3 cpu.py +enabled=true \ No newline at end of file diff --git a/uwidgets/official-widgets/spotify/settings.ini b/uwidgets/official-widgets/spotify/settings.ini new file mode 100644 index 000000000..4784595b6 --- /dev/null +++ b/uwidgets/official-widgets/spotify/settings.ini @@ -0,0 +1,5 @@ +[settings] +# supported options for gravity: NORTH_WEST, NORTH, NORTH_EAST, WEST, CENTER, EAST, SOUTH_WEST, SOUTH, SOUTH_EAST (default) +gravity=SOUTH_EAST +margin_x=20 +margin_y=20 \ No newline at end of file diff --git a/uwidgets/official-widgets/spotify/spotify.py b/uwidgets/official-widgets/spotify/spotify.py new file mode 100755 index 000000000..a8e4c8625 --- /dev/null +++ b/uwidgets/official-widgets/spotify/spotify.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 + +""" +This file is part of "blighty" and "uwidgets" which is released under GPL. + +See file LICENCE or go to http://www.gnu.org/licenses/ for full license +details. + +uwidgets is a desktop widget creation and management library for Python 3. + +Copyright (c) 2022 Rudra Saraswat <rs2009@ubuntu.com>. +Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +All rights reserved. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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/>. +""" + +from uwidgets import CanvasGravity +from uwidgets.x11 import Canvas, start_event_loop +from uwidgets import CanvasType + +import cairo +import shutil +import requests +import subprocess +import configparser + +from gi.repository import GLib + +from PIL import Image +from pydbus import SessionBus + + +class Fonts: + UBUNTU_NORMAL = "Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.NORMAL + + +class SpotifyDBus: + def __init__(self): + self.proxy = SessionBus().get( + 'org.mpris.MediaPlayer2.spotify', + '/org/mpris/MediaPlayer2' + ) + + def get_metadata(self): + return {k.split(':')[1]: v for k, v in self.proxy.Metadata.items()} + + def toggle_play(self): + self.proxy.PlayPause() + + def is_paused(self): + return self.proxy.PlaybackStatus == "Paused" + + +class Spotify(Canvas): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.init_dbus() + + def init_dbus(self): + try: + self.spotify = SpotifyDBus() + except GLib.Error: + self.spotify = None + + self.last_art_url = "" + + def on_button_pressed(self, button, state, x, y): + if button == 1: # Left button + self.spotify.toggle_play() + elif button == 3: # Right button + subprocess.run(['spotify']) + + def draw_background(ctx): + size = ctx.canvas.get_size() + ctx.rectangle(0, 0, *size) + ctx.set_source_rgba(0, 0, 0, 0.6) + ctx.fill() + + def draw_decoration(ctx): + ctx.rectangle(0, 0, ctx.canvas.width, 3) + ctx.set_source_rgba(1, 1, 1, 1) + ctx.fill() + + def draw_art(ctx, url, pause): + temp_file = "/tmp/spotify_art" + temp_img = temp_file + ".png" + + if ctx.canvas.last_art_url != url: + response = requests.get(url, stream=True) + with open(temp_file, 'wb') as out_file: + shutil.copyfileobj(response.raw, out_file) + del response + ctx.canvas.last_art_url = url + + # Ensure it is a PNG image + size = min(ctx.canvas.width, ctx.canvas.height) + image = Image.open(temp_file) + image.thumbnail((size, size), Image.ANTIALIAS) + image.save(temp_img) + + surface = cairo.ImageSurface.create_from_png(temp_img) + ctx.set_source_surface(surface, 0, 0) + ctx.paint() + + if pause: + ctx.set_source_rgba(0, 0, 0, 0.75) + ctx.rectangle(0, 0, size, size) + ctx.fill() + ctx.set_source_rgba(.8, 0.8, 0.8, 0.5) + size2 = size >> 1 + size4 = size2 >> 1 + size5 = size // 5 + ctx.rectangle(size4, size4, size5, size2) + ctx.rectangle(size - size4 - size5, size4, size5, size2) + ctx.fill() + + def draw_metadata(ctx, metadata): + ctx.set_source_rgb(0.9, 0.9, 0.9) + + ctx.select_font_face(*Fonts.UBUNTU_NORMAL) + ctx.set_font_size(24) + ctx.move_to(112, 42) + ctx.show_text(metadata["title"]) + + ctx.set_source_rgb(0.5, 0.5, 0.5) + ctx.set_font_size(15) + ctx.move_to(112, 72) + ctx.show_text("{artists} ({album})".format( + artists = ", ".join(metadata["artist"]), + album = metadata["album"] + )) + + def on_draw(self, ctx): + if self.spotify is None: + self.init_dbus() + return + + try: + metadata = self.spotify.get_metadata() + except GLib.Error: + self.spotify = None + return + + _ctx = ctx + ctx.draw_background() + ctx.draw_art(metadata["artUrl"], self.spotify.is_paused()) + ctx.draw_metadata(metadata) + ctx.draw_decoration() + + +if __name__ == "__main__": + config = configparser.ConfigParser() + config.read('settings.ini') + spotify = Spotify( + x = int(config.get('settings', 'margin_x')), + y = int(config.get('settings', 'margin_y')), + width = 540, + height = 96, + gravity = getattr(CanvasGravity, config.get('settings', 'gravity')), + interval = 1000 + ) + + spotify.show() + start_event_loop() diff --git a/uwidgets/official-widgets/spotify/widget.ini b/uwidgets/official-widgets/spotify/widget.ini new file mode 100644 index 000000000..3103638b5 --- /dev/null +++ b/uwidgets/official-widgets/spotify/widget.ini @@ -0,0 +1,5 @@ +[widget] +name=Spotify +summary=A Spotify (music) widget for the Unity desktop. +exec=python3 spotify.py +enabled=true \ No newline at end of file diff --git a/uwidgets/official-widgets/unsplash-background/settings.ini b/uwidgets/official-widgets/unsplash-background/settings.ini new file mode 100644 index 000000000..3ba5536b3 --- /dev/null +++ b/uwidgets/official-widgets/unsplash-background/settings.ini @@ -0,0 +1,5 @@ +[settings] +# supported options for gravity: NORTH_WEST, NORTH, NORTH_EAST, WEST, CENTER, EAST, SOUTH_WEST, SOUTH, SOUTH_EAST (default) +gravity=SOUTH_EAST +margin_x=20 +margin_y=440 \ No newline at end of file diff --git a/uwidgets/official-widgets/unsplash-background/unsplash-background.py b/uwidgets/official-widgets/unsplash-background/unsplash-background.py new file mode 100755 index 000000000..80363cbcc --- /dev/null +++ b/uwidgets/official-widgets/unsplash-background/unsplash-background.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +""" +This file is part of "blighty" and "uwidgets" which is released under GPL. + +See file LICENCE or go to http://www.gnu.org/licenses/ for full license +details. + +uwidgets is a desktop widget creation and management library for Python 3. + +Copyright (c) 2022 Rudra Saraswat <rs2009@ubuntu.com>. +Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +All rights reserved. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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/>. +""" + +from uwidgets import CanvasGravity +from uwidgets.x11 import Canvas, start_event_loop +from uwidgets.settings import UnityWallpaper + +import cairo +import subprocess +import configparser + +from gi.repository import GLib + + +class Fonts: + UBUNTU_NORMAL = "Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.NORMAL + + +class UnBackground(Canvas): + def on_button_pressed(self, button, state, x, y): + if button == 1: # Left button + self.change_wallpaper() + elif button == 3: # Right button + subprocess.run(['unity-control-center', 'appearance']) + + def change_wallpaper(ctx): + UnityWallpaper().set_wallpaper_from_url("https://source.unsplash.com/random/3840x2160") + + def draw_background(ctx): + size = ctx.canvas.get_size() + ctx.rectangle(0, 0, *size) + ctx.set_source_rgba(0, 0, 0, 0.6) + ctx.fill() + + def draw_decoration(ctx): + ctx.rectangle(0, 0, ctx.canvas.width, 3) + ctx.set_source_rgba(1, 1, 1, 1) + ctx.fill() + + def draw_info(ctx): + ctx.set_source_rgb(0.9, 0.9, 0.9) + + ctx.select_font_face(*Fonts.UBUNTU_NORMAL) + ctx.set_font_size(24) + ctx.move_to(20, 42) + ctx.show_text('Unsplash | Random Background') + + ctx.set_source_rgb(0.5, 0.5, 0.5) + ctx.set_font_size(15) + ctx.move_to(20, 64) + ctx.show_text('Click this to set a random Unsplash background (requires internet).') + + def on_draw(self, ctx): + ctx.draw_background() + ctx.draw_decoration() + ctx.draw_info() + + +if __name__ == "__main__": + config = configparser.ConfigParser() + config.read('settings.ini') + unbg = UnBackground( + x = int(config.get('settings', 'margin_x')), + y = int(config.get('settings', 'margin_y')), + width = 540, + height = 80, + gravity = getattr(CanvasGravity, config.get('settings', 'gravity')), + interval = 1000 + ) + + unbg.show() + start_event_loop() diff --git a/uwidgets/official-widgets/unsplash-background/widget.ini b/uwidgets/official-widgets/unsplash-background/widget.ini new file mode 100644 index 000000000..d997844a0 --- /dev/null +++ b/uwidgets/official-widgets/unsplash-background/widget.ini @@ -0,0 +1,5 @@ +[widget] +name=Unsplash Background +summary=A widget to set a random Unsplash wallpaper for the Unity desktop. +exec=python3 unsplash-background.py +enabled=true \ No newline at end of file diff --git a/uwidgets/setup.py b/uwidgets/setup.py new file mode 100755 index 000000000..49bef351e --- /dev/null +++ b/uwidgets/setup.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +""" +This file is part of "uwidgets" which is released under GPL. + +See file LICENCE or go to http://www.gnu.org/licenses/ for full license +details. + +uwidgets is a desktop widget creation and management library for Python 3. + +Copyright (c) 2022 Rudra Saraswat <rs2009@ubuntu.com>. +Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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/>. +""" + +from setuptools import Extension, find_packages, setup + +x11 = Extension('uwidgets._x11', + include_dirs = ['/usr/include/cairo/'], + libraries = ['cairo', 'X11', 'Xinerama'], + extra_compile_args = ['-std=c99'], + sources = [ + 'uwidgets/x11/_x11module.c', + 'uwidgets/x11/atelier.c', + 'uwidgets/x11/base_canvas.c', + ] +) + + +setup( + name = 'uwidgets', + version = '1.0.0', + description = 'Desktop Widget Manager for Unity, based on Blighty.', + author = 'Rudra Saraswat', + author_email = 'rs2009@ubuntu.com', + url = 'https://unityd.org', + classifiers=[ + 'Development Status :: 5 - Production/Stable', + + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Build Tools', + + 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', + + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.11', + ], + keywords = 'desklet widget infotainment', + packages = find_packages(exclude=['contrib', 'docs']), + ext_modules = [x11], + install_requires = ['pycairo'], + scripts = ['uwidgets-runner'], +) diff --git a/uwidgets/uwidgets-runner b/uwidgets/uwidgets-runner new file mode 100755 index 000000000..3efeac56e --- /dev/null +++ b/uwidgets/uwidgets-runner @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +import os +import configparser + +widgets=[] +widgets_dir=os.path.expanduser('~/.local/share/unity/widgets') + +if os.path.exists(widgets_dir): + for widget in [f.path for f in os.scandir(widgets_dir) if f.is_dir() and os.path.exists(os.path.join(f, 'widget.ini'))]: + try: + os.chdir(widget) + config = configparser.ConfigParser() + config.read(os.path.join(widget, 'widget.ini')) + if config.get('widget', 'enabled') == 'true': + os.popen(config.get('widget', 'exec')) + widgets.append(os.path.basename(widget)) + except (KeyError, configparser.NoSectionError, configparser.NoOptionError) as e: + print(f'uwidget-runner: error occurred when attempting to run {widget}:\n {e}') + +if len(widgets) == 0: + print("No widgets found.") +else: + print("Loaded:", ' '.join(widgets)) diff --git a/uwidgets/uwidgets-runner.desktop b/uwidgets/uwidgets-runner.desktop new file mode 100644 index 000000000..2616869c3 --- /dev/null +++ b/uwidgets/uwidgets-runner.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=UWidgets Runner +Comment=Start UWidgets at login +Exec=uwidgets-runner +OnlyShowIn=Unity +X-GNOME-AutoRestart=true +X-GNOME-Autostart-Delay=2 +NoDisplay=false \ No newline at end of file diff --git a/uwidgets/uwidgets/__init__.py b/uwidgets/uwidgets/__init__.py new file mode 100644 index 000000000..ae9dc085a --- /dev/null +++ b/uwidgets/uwidgets/__init__.py @@ -0,0 +1,77 @@ +# This file is part of "blighty" and "uwidgets" which is released under GPL. +# +# See file LICENCE or go to http://www.gnu.org/licenses/ for full license +# details. +# +# uwidgets is a desktop widget creation and management library for Python 3. +# +# Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +# All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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/>. + +""" +This module contains the common objects and types for the different kind of +canvases provided by ``uwidgets``. +""" + +# XWayland fix +import os +os.environ["GDK_BACKEND"] = "x11" + +from . _extended_context import ExtendedContext + +from . _brush import brush, TextAlign + + +class CanvasType: + """The Canvas type. + + The canvas types enumerated in this Python type reflect the same window + types that one can request to the window manager via the `Extended + Window Manager Hints <https://standards.freedesktop.org/wm-spec/wm-spec-1.3.html>`_. + + - ``NORMAL`` is a normal top-level window. + - ``DESKTOP`` is a window drawn directly on the desktop. + - ``DOCK`` indicates a dock or panel window that will usually stay on top + of other windows. + - ``UNDECORATED`` is a type of window that behaves as a toolbar. As such, + it is undecorated. + """ + + NORMAL = 0 # _NET_WM_WINDOW_TYPE_NORMAL + DESKTOP = 1 # _NET_WM_WINDOW_TYPE_DESKTOP + DOCK = 2 # _NET_WM_WINDOW_TYPE_DOCK + UNDECORATED = 3 # _NET_WM_WINDOW_TYPE_TOOLBAR + + +class CanvasGravity: + """Window gravity control type. + + The positioning of a canvas on the screen is controlled by its gravity. + By default, a window is positioned in a coordinate system where the origin + is located in the top-left corner of the screen, with the *x* axis running + horizontally from left to right, and the *y* from top to bottom. To change + the location of the origin, use one of the following values. + """ + + NORTH_WEST = 1 + NORTH = 2 + NORTH_EAST = 3 + WEST = 4 + CENTER = 5 + EAST = 6 + SOUTH_WEST = 7 + SOUTH = 8 + SOUTH_EAST = 9 + STATIC = 10 diff --git a/uwidgets/uwidgets/_brush.py b/uwidgets/uwidgets/_brush.py new file mode 100644 index 000000000..763690aaf --- /dev/null +++ b/uwidgets/uwidgets/_brush.py @@ -0,0 +1,163 @@ +""" +This file is part of "blighty" "uwidgets" which is released under GPL. + +See file LICENCE or go to http://www.gnu.org/licenses/ for full license +details. + +uwidgets is a desktop widget creation and management library for Python 3. + +Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +All rights reserved. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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/>. +""" + +from functools import wraps + + +def not_callable_from_instance(*args, **kwargs): + raise RuntimeError( + "Method not callable on instances of type {}".format( + type(args[0]).__name__ + ) + ) + + +class BrushSets: + brush_sets = {} + inherited = {} + + @staticmethod + def add_brush(brush_set, method_name, method): + if brush_set not in BrushSets.brush_sets: + BrushSets.brush_sets[brush_set] = {} + + BrushSets.brush_sets[brush_set][method_name] = method + + @staticmethod + def get_brush_set(brush_set): + return BrushSets.brush_sets.get(brush_set, {}) + + @staticmethod + def inherit(klass): + if klass.__qualname__ in BrushSets.inherited: + return + + for e in klass.__bases__: + try: + BrushSets.inherit(e) + if klass.__qualname__ not in BrushSets.brush_sets: + BrushSets.brush_sets[klass.__qualname__] = {} + BrushSets.brush_sets[klass.__qualname__].update(BrushSets.brush_sets[e.__qualname__]) + except KeyError: + # No brushes registered for the superclass so we can skip it + pass + + BrushSets.inherited[klass.__qualname__] = True + + +def brush(f): + """Brush decorator. + + Used to mark a bound method of a subclass of the `Canvas` class as a + _brush_. The method is then rebound to to the extended Cairo context that + is passed to the `on_draw` callback. The first argument should then be + called `ctx` or `cr` instead of `self`, but this is not enforced so that + any keyword can be chosen. + + Example: + class BrushExample(uwidgets.x11.Canvas): + @brush + def brush_method(ctx, data): + ctx.save() + # Draw something on the Cairo context + ctx.restore() + + def on_draw(self, ctx): + ctx.brush_method(42) + + In the above example, the `brush_method` has been decorated with the + `brush` decorator. Therefore it will be callable as a method of `ctx` + rather than `self`. An attempt to call it from `self` will cause a + `RuntimeError` since the method is now bound to `ctx`. + + The use of the `brush` decorator is not restricted to X11 canvases. + """ + BrushSets.add_brush(*f.__qualname__.rsplit('.', 1), method = f) + + @wraps(f) + def wrapper(*args, **kwargs): + return not_callable_from_instance(*args, **kwargs) + + return wrapper + + +## Basic brushes ############################################################## + +def draw_grid(ctx, x = 50, y = 50): + w, h = ctx.canvas.get_size() + + ctx.save() + + ctx.set_source_rgba(.8, .8, .8, .8) + ctx.set_line_width(1) + ctx.set_font_size(9) + for i in range(x, w, x): + ctx.write_text(i, 0, str(i), align = TextAlign.BOTTOM_MIDDLE) + ctx.move_to(i, 8) + ctx.line_to(i, h) + ctx.stroke() + + for i in range(y, h, y): + ctx.write_text(0, i, str(i), align = TextAlign.CENTER_LEFT) + ctx.move_to(8, i) + ctx.line_to(w, i) + ctx.stroke() + + ctx.restore() + + +class TextAlign: + TOP_RIGHT = 1 + TOP_MIDDLE = 2 + TOP_LEFT = 3 + CENTER_RIGHT = 4 + CENTER_MIDDLE = 5 + CENTER_LEFT = 6 + BOTTOM_RIGHT = 7 + BOTTOM_MIDDLE = 8 + BOTTOM_LEFT = 9 + + +def write_text(cr, x, y, text, align = TextAlign.TOP_LEFT): + ex = cr.text_extents(text) + + if align <= TextAlign.TOP_LEFT: + dy = 0 + elif align <= TextAlign.CENTER_LEFT: + dy = ex.height // 2 + else: + dy = ex.height + + if align % 3 == 1: + dx = ex.width + elif align % 3 == 2: + dx = ex.width // 2 + else: + dx = 0 + + cr.move_to(x - dx, y + dy) + cr.show_text(text) + cr.stroke() + + return ex diff --git a/uwidgets/uwidgets/_extended_context.py b/uwidgets/uwidgets/_extended_context.py new file mode 100644 index 000000000..c5517b9fd --- /dev/null +++ b/uwidgets/uwidgets/_extended_context.py @@ -0,0 +1,72 @@ +""" +This file is part of "blighty" and "uwidgets" which is released under GPL. + +See file LICENCE or go to http://www.gnu.org/licenses/ for full license +details. + +uwidgets is a desktop widget creation and management library for Python 3. + +Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +All rights reserved. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +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/>. +""" + +from . _brush import BrushSets, not_callable_from_instance + + +class ExtendedContext: + """Extension of the standard `cairo.Context` class. + + This class is used to extend the vanilla `cairo.Context` with _brushes_. + These are either methods of a subclass of `Canvas` that are prefixed with + `draw_`, or those that are explicitly decorated with the `@brush` + decorator. + + There shouldn't be any reasons why you'd want to use instantiate this class + directly. The `ctx` argument that is passed to the `Canvas` `on_draw` + callback is an instance of this class. This can be passed as an argument + to any callable object that expects a `cairo.Context` instance. The + underlying `Canvas` object can be accessed via the `canvas` attribute. This + can be useful if one needs to refer to the parent canvas geometry (e.g. + its size). + """ + def __init__(self, ctx, canvas): + self._ctx = ctx + self.canvas = canvas + + collected_methods = [] + + for dm in dir(canvas): + try: + if callable(getattr(canvas, dm)) and dm[:5] == "draw_": + collected_methods.append(dm) + except RuntimeError: + # In the GTK case, introspection breaks getattr so we ignore + # the attributes we cannot retrieve. + pass + + # for m in [dm for dm in dir(canvas) if callable(getattr(canvas, dm)) and dm[:5] == "draw_"]: + for m in collected_methods: + # Re-bind brush method and mark the original as non-callable + setattr(self, m, getattr(type(canvas), m).__get__(self, ExtendedContext)) + setattr(canvas, m, not_callable_from_instance.__get__(canvas, type(canvas))) + + for n, m in BrushSets.get_brush_set(type(canvas).__qualname__).items(): + if n in dir(ctx): + raise RuntimeError("Brush name '{}' clashes with attribute or method in {}".format(n, type(ctx).__qualname__)) + setattr(self, n, m.__get__(self, ExtendedContext)) + + def __getattr__(self, name): + """Access the underling context methods.""" + return getattr(self._ctx, name) diff --git a/uwidgets/uwidgets/legacy.py b/uwidgets/uwidgets/legacy.py new file mode 100644 index 000000000..25b4109b6 --- /dev/null +++ b/uwidgets/uwidgets/legacy.py @@ -0,0 +1,73 @@ +# This file is part of "blighty" and "uwidgets" which is released under GPL. +# +# See file LICENCE or go to http://www.gnu.org/licenses/ for full license +# details. +# +# uwidgets is a desktop widget creation and management library for Python 3. +# +# Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +# All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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/>. + +"""Legacy visual tools. + +Most of the classes in this module are inspired from Conky. +""" + +from collections import deque + + +class Graph: + """A uwidgets take on Conky graphsself. + + The constructor allows you to specify where the graph should be located + as well as its size. You push values to it by calling the ``push_value`` + method. By default, the values are assumed to be in the range from 0 to + 100. If this is not the case, you can change the Y scale by specifying a + value for the ``scale`` keyword argument. + """ + def __init__(self, x, y, width, height, scale=100): + self.x = x + self.y = y + self.width = width + self.height = height + self.scale = scale if scale else 0 + self.auto = not scale + self._values = deque(maxlen=width) + + def push_value(self, v): + self._values.append(v) + if self.auto: + self.scale = max(self._values) + + def draw(self, cr): + if not self.scale: + return + + offset = self.x + self.width - len(self._values) + for i in range(len(self._values)): + cr.set_line_width(1) + if self.height > 0: + cr.move_to(offset + i + .5, self.y + self.height) + cr.line_to( + offset + i + .5, + self.y + int(self.height * (1-self._values[i]/self.scale)) + ) + else: + cr.move_to(offset + i + .5, self.y) + cr.line_to( + offset + i + .5, + self.y + int(-self.height * self._values[i] / self.scale) + ) + cr.stroke() diff --git a/uwidgets/uwidgets/settings/__init__.py b/uwidgets/uwidgets/settings/__init__.py new file mode 100644 index 000000000..b8410bcab --- /dev/null +++ b/uwidgets/uwidgets/settings/__init__.py @@ -0,0 +1,4 @@ +from uwidgets.settings import * + +from . launcher import UnityLauncher +from . wallpaper import UnityWallpaper \ No newline at end of file diff --git a/uwidgets/uwidgets/settings/launcher.py b/uwidgets/uwidgets/settings/launcher.py new file mode 100644 index 000000000..c5d7151d0 --- /dev/null +++ b/uwidgets/uwidgets/settings/launcher.py @@ -0,0 +1,18 @@ +import os +from gi.repository import Gio, GLib + + +class UnityLauncher: + launcher_settings = Gio.Settings.new('com.canonical.Unity.Launcher') + launcher_compiz_settings = Gio.Settings.new_with_path('org.compiz.unityshell', '/org/compiz/profiles/unity/plugins/unityshell/') + + def set_launcher_position(self, position: str): + if position == 'Bottom' or position == 'bottom': + self.launcher_settings['launcher-position'] = 'Bottom' + elif position == 'Left' or position == 'left': + self.launcher_settings['launcher-position'] = 'Left' + + def set_launcher_autohide(self, hidemode: bool): + self.launcher_compiz_settings['launcher-hide-mode'] = hidemode + +# XXX: Add more settings diff --git a/uwidgets/uwidgets/settings/wallpaper.py b/uwidgets/uwidgets/settings/wallpaper.py new file mode 100644 index 000000000..f5f8c95b9 --- /dev/null +++ b/uwidgets/uwidgets/settings/wallpaper.py @@ -0,0 +1,27 @@ +import os +from gi.repository import Gio, GLib + +import requests + + +class UnityWallpaper: + cache_dir = GLib.get_user_cache_dir() + pictures_dir = GLib.get_user_special_dir(GLib.USER_DIRECTORY_PICTURES) + splash_wallpaper_file_path = os.path.join( + cache_dir, + "org.unityd.wallpapers" + ) + background_settings = Gio.Settings.new("org.gnome.desktop.background") + + def set_wallpaper_from_file_uri(self, file_uri: str): + self.background_settings['picture-uri'] = f"file:///{file_uri}" + + def write_image_url_to_wallpaper_file(self, image_url: str): + response = requests.get(image_url, stream=True) + if response.ok: + with open(self.splash_wallpaper_file_path, "wb") as wallpaper_path: + wallpaper_path.write(response.raw.read()) + + def set_wallpaper_from_url(self, image_url: str): + self.write_image_url_to_wallpaper_file(image_url) + self.set_wallpaper_from_file_uri(self.splash_wallpaper_file_path) diff --git a/uwidgets/uwidgets/x11/__init__.py b/uwidgets/uwidgets/x11/__init__.py new file mode 100644 index 000000000..e2a032d80 --- /dev/null +++ b/uwidgets/uwidgets/x11/__init__.py @@ -0,0 +1,31 @@ +# This file is part of "blighty" and "uwidgets" which is released under GPL. +# +# See file LICENCE or go to http://www.gnu.org/licenses/ for full license +# details. +# +# uwidgets is a desktop widget creation and management library for Python 3. +# +# Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +# All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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/>. + +""" +This module provides support for creating X11 canvases. If you are trying to +replicate conky's behaviour, the API offered by this module is the closest to +it. +""" + +from uwidgets._x11 import * + +from . canvas import Canvas diff --git a/uwidgets/uwidgets/x11/_x11module.c b/uwidgets/uwidgets/x11/_x11module.c new file mode 100644 index 000000000..31cb4132d --- /dev/null +++ b/uwidgets/uwidgets/x11/_x11module.c @@ -0,0 +1,71 @@ +// This file is part of "blighty" and "uwidgets" which is released under GPL. +// +// See file LICENCE or go to http://www.gnu.org/licenses/ for full license +// details. +// +// uwidgets is a desktop widget creation and management library for Python 3. +// +// Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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/>. + +#include <Python.h> +#include "pycairo.h" + +#include "atelier.h" + +extern PyTypeObject BaseCanvasType; + + +static PyMethodDef x11methods[] = { + { + "start_event_loop", + Atelier_start_event_loop, + METH_NOARGS, + "Starts the main event loop for all the BaseCanvas objects." + }, + {NULL, NULL, 0, NULL} +}; + +static PyModuleDef x11module = { + PyModuleDef_HEAD_INIT, + "_x11", + "C X11 support module for uwidgets.", + -1, + x11methods, // m_methods + NULL, NULL, NULL, NULL +}; + +PyMODINIT_FUNC +PyInit__x11(void) +{ + PyObject* m; + + if (PyType_Ready(&BaseCanvasType) < 0) + return NULL; + + m = PyModule_Create(&x11module); + if (m == NULL) + return NULL; + + if (import_cairo() < 0) + return NULL; + + Py_INCREF(&BaseCanvasType); + PyModule_AddObject(m, "BaseCanvas", (PyObject *)&BaseCanvasType); + + // Initialise Atelier + Atelier_init(); + return m; +} diff --git a/uwidgets/uwidgets/x11/atelier.c b/uwidgets/uwidgets/x11/atelier.c new file mode 100644 index 000000000..0f229e226 --- /dev/null +++ b/uwidgets/uwidgets/x11/atelier.c @@ -0,0 +1,265 @@ +// This file is part of "blighty" and "uwidgets" which is released under GPL. +// +// See file LICENCE or go to http://www.gnu.org/licenses/ for full license +// details. +// +// uwidgets is a desktop widget creation and management library for Python 3. +// +// Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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/>. + + +#include "atelier.h" +#include <stdio.h> + +#define LOOP_INTERVAL 2000 + +static PyObject * atelier = NULL; + +static Display * display = NULL; +static XineramaScreenInfo * info = NULL; +static int n_scr = 0; + + +// ---------------------------------------------------------------------------- +static PyObject * +get_callback(PyObject * object, char * method) { + PyObject * py_method = PyUnicode_FromString(method); + + return PyObject_HasAttr(object, py_method) > 0 + ? PyObject_GetAttr(object, py_method) + : NULL; +} + + +// ---------------------------------------------------------------------------- +Display * +Atelier_get_display(void) { + if (display == NULL) + Atelier_set_display(XOpenDisplay(NULL)); + + return display; +} + + +// ---------------------------------------------------------------------------- +void +Atelier_set_display(Display * d) { + if (d == NULL) { + if (display != NULL) { + if (info != NULL) { + XFree(info); + info = NULL; + n_scr = 0; + } + + XCloseDisplay(display); + display = NULL; + } + return; + } + + display = d; + + // Xinerama support + int event, error; + + if (!XineramaQueryExtension(d, &event, &error)) + return; + + if (!XineramaIsActive(d)) + return; + + info = XineramaQueryScreens(d, &n_scr); +} + + +// ---------------------------------------------------------------------------- +XineramaScreenInfo * +Atelier_get_screen_info(int screen) { + if (screen < n_scr) { + for (register int i = 0; i < n_scr; i++) + if (screen == info[i].screen_number) + return &(info[i]); + } + + return NULL; +} + + +// ---------------------------------------------------------------------------- +void +Atelier_init(void) { + // Initialise Xlib and CPython for concurrent threads. + XInitThreads(); + PyEval_InitThreads(); + + // Initialise atelier to an empty list + if (atelier != NULL) { + Py_DECREF(atelier); + } + atelier = PyList_New(0); +} + + +// ---------------------------------------------------------------------------- +void +Atelier_add_canvas(BaseCanvas * canvas) { + // TODO: Check that the canvas is not registered already. + PyList_Append(atelier, (PyObject*) canvas); +} + + +// ---------------------------------------------------------------------------- +int +Atelier_remove_canvas(BaseCanvas * canvas) { + BaseCanvas * c; + for (int i = 0; i < PyList_Size(atelier); i++) { + c = (BaseCanvas *) PyList_GetItem(atelier, i); + if (c == canvas) { + PyList_SetSlice(atelier, i, i + 1, NULL); + int n_canvas = PyList_Size(atelier); + if (!n_canvas) + Atelier_set_display(NULL); + return n_canvas; + } + } + return -1; +} + + +/****************************************************************************** + ** EVENT LOOP + ******************************************************************************/ + +static int main_loop_running = 0; + + +// ---------------------------------------------------------------------------- +static void +dispatch_event(BaseCanvas * canvas, XEvent * e) { + char keybuf[8]; + KeySym key; + PyObject * cb; + + switch (e->type) { + case ClientMessage: + // TODO: Extend + if ((Atom) e->xclient.data.l[0] == canvas->wm_delete_window) { + PyObject_CallMethod((PyObject *) canvas, "destroy", NULL); + } + return; + + case ButtonPress: + cb = get_callback((PyObject *) canvas, "on_button_pressed"); + if (cb != NULL) { + PyObject_CallObject(cb, Py_BuildValue("(iiii)", + e->xbutton.button, + e->xbutton.state, + e->xbutton.x, + e->xbutton.y + )); + } + return; + + case KeyPress: + cb = get_callback((PyObject *) canvas, "on_key_pressed"); + if (cb != NULL) { + XLookupString(&(e->xkey), keybuf, sizeof(keybuf), &key, NULL); + PyObject_CallObject(cb, Py_BuildValue("(ii)", + key, + e->xkey.state + )); + } + return; + + case Expose: + if (e->xexpose.count == 0) { + if (canvas->_needs_redraw != 0) { + BaseCanvas__on_draw(canvas, canvas->context_arg); + canvas->_needs_redraw = 0; + } + + // Only clear the window when we are sure we are ready to paint. + BaseCanvas__redraw(canvas); + + if (PyErr_Occurred() != NULL) { + PyErr_Print(); + PyObject_CallMethod((PyObject *) canvas, "dispose", NULL); + break; + } + } + return; + + default: + fprintf(stderr, "Dropping unhandled XEevent.type = %d.\n", e->type); + } +} + + +// ---------------------------------------------------------------------------- +PyObject * +Atelier_start_event_loop(PyObject * args, PyObject * kwargs) { + if (main_loop_running > 0 || atelier == NULL) { + Py_INCREF(Py_None); return Py_None; + } + + main_loop_running = 1; + + XEvent e; + BaseCanvas * canvas; + while (main_loop_running != 0 && PyList_Size(atelier) > 0 && display != NULL) { + Py_BEGIN_ALLOW_THREADS + XNextEvent(display, &e); + Py_END_ALLOW_THREADS + + if (e.type >= LASTEvent) continue; + // Find the canvas based on window ID + int found = 0; + for (int i = 0; i < PyList_Size(atelier); i++) { + canvas = (BaseCanvas *) PyList_GetItem(atelier, i); + if (canvas->win_id == e.xany.window) { + found = 1; + break; + } + } + + // TODO: Raise RuntimeError! + if (!found) { + fprintf(stderr, "Canvas not found!\n"); + return NULL; + } + + dispatch_event(canvas, &e); + } + + main_loop_running = 0; + + Py_INCREF(Py_None); return Py_None; +} + + +// ---------------------------------------------------------------------------- +void +Atelier_stop_event_loop(void) { + main_loop_running = 0; +} + + +// ---------------------------------------------------------------------------- +int +Atelier_is_running(void) { + return main_loop_running; +} diff --git a/uwidgets/uwidgets/x11/atelier.h b/uwidgets/uwidgets/x11/atelier.h new file mode 100644 index 000000000..61bf704c2 --- /dev/null +++ b/uwidgets/uwidgets/x11/atelier.h @@ -0,0 +1,63 @@ +// This file is part of "uwidgets" which is released under GPL. +// +// See file LICENCE or go to http://www.gnu.org/licenses/ for full license +// details. +// +// uwidgets is a desktop widget creation and management library for Python 3. +// +// Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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/>. + + +#ifndef ATELIER_H +#define ATELIER_H + +#include "base_canvas.h" + +#include <X11/extensions/Xinerama.h> // Must be included AFTER base_canvas! + +Display * +Atelier_get_display(void); + +void +Atelier_set_display(Display *); + +XineramaScreenInfo * +Atelier_get_screen_info(int); + +void +Atelier_init(void); + +void +Atelier_add_canvas(BaseCanvas * canvas); + +int +Atelier_remove_canvas(BaseCanvas * canvas); + + +/****************************************************************************** + ** EVENT LOOP + ******************************************************************************/ + +PyObject * +Atelier_start_event_loop(PyObject *, PyObject *); + +void +Atelier_stop_event_loop(void); + +int +Atelier_is_running(void); + +#endif diff --git a/uwidgets/uwidgets/x11/base_canvas.c b/uwidgets/uwidgets/x11/base_canvas.c new file mode 100644 index 000000000..20b9ae54e --- /dev/null +++ b/uwidgets/uwidgets/x11/base_canvas.c @@ -0,0 +1,445 @@ +// This file is part of "uwidgets" which is released under GPL. +// +// See file LICENCE or go to http://www.gnu.org/licenses/ for full license +// details. +// +// uwidgets is a desktop widget creation and management library for Python 3. +// +// Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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/>. + +#define BASE_CANVAS_C + + +#include "atelier.h" +#include "base_canvas.h" + +#include <X11/extensions/Xinerama.h> + +#define PYCAIRO_NO_IMPORT +#include "pycairo.h" +#include "pythread.h" + + +// +// CONSTANTS +// +#define UI_INTERVAL 1000 // 1 ms + +static const char * WINDOW_TYPE_MAP[] = { + "_NET_WM_WINDOW_TYPE_NORMAL", + "_NET_WM_WINDOW_TYPE_DESKTOP", + "_NET_WM_WINDOW_TYPE_DOCK", + "_NET_WM_WINDOW_TYPE_TOOLBAR" +}; + + +// +// PRIVATE GLOBAL STATE +// +static XVisualInfo visualinfo; +static XSetWindowAttributes attr; + + +// +// LOCAL HELPERS +// + +// ---------------------------------------------------------------------------- +static time_t +gettime(void) { + struct timespec ts; + clock_gettime(CLOCK_BOOTTIME, &ts); + return ts.tv_sec * 1000 + ts.tv_nsec / 1e6; +} + + +// ---------------------------------------------------------------------------- +static void +BaseCanvas__change_property(BaseCanvas * self, const char * property_name, const char * property_value, int mode) { + Display * display = Atelier_get_display(); + + Atom value = XInternAtom(display, property_value, False); + XChangeProperty( + display, + self->win_id, + XInternAtom(display, property_name, False), + XA_ATOM, + 32, + mode, + (unsigned char *) &value, + 1 + ); +} + + +// ---------------------------------------------------------------------------- +void +BaseCanvas__redraw(BaseCanvas * self) { + cairo_save(self->context); + cairo_set_operator(self->context, CAIRO_OPERATOR_SOURCE); + cairo_paint(self->context); + cairo_restore(self->context); + XFlush(Atelier_get_display()); +} + + +// ---------------------------------------------------------------------------- +void +BaseCanvas__on_draw(BaseCanvas * self, PyObject * args) { + PyObject * cb = PyObject_GetAttr((PyObject *) self, PyUnicode_FromString("_on_draw")); + + if (cb == NULL) { + PyErr_SetString( + PyExc_TypeError, + "Subclasses of BaseCanvas must implement the 'on_draw(self, context)' method." + ); + return; + } + else { + if (!PyCallable_Check(cb)) { + PyErr_SetString(PyExc_TypeError, "on_draw callback must be callable."); + return; + } + + // Required for animations in order to avoid flickers. + // The X server queues up draw requests. This way we group + // them together and we send a single draw request + cairo_push_group(self->context); + + // Call user declaration of the 'on_draw' method + PyObject * cb_result = PyObject_CallObject(cb, args); + + cairo_pattern_t * group = cairo_pop_group(self->context); + if (cb_result == Py_None) + cairo_set_source(self->context, group); + cairo_pattern_destroy(group); + } +} + + +// ---------------------------------------------------------------------------- +static void +BaseCanvas__ui_thread(BaseCanvas * self) { + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); + + self->context_arg = Py_BuildValue("(O)", PycairoContext_FromContext( + self->context, &PycairoContext_Type, (PyObject*) NULL + )); + + self->_expiry = gettime(); + + while (self->_running) { + Py_BEGIN_ALLOW_THREADS + usleep(self->interval > 100 ? 100 * UI_INTERVAL : UI_INTERVAL); + Py_END_ALLOW_THREADS + + if (Atelier_is_running() > 0 && self->_expiry <= gettime()) { + self->_needs_redraw = 1; + + // Request redraw + XEvent event; + event.type = Expose; + event.xany.window = self->win_id; + event.xexpose.count = 0; + + Display * display = Atelier_get_display(); + XLockDisplay(display); + XSendEvent(display, self->win_id, False, ExposureMask, &event); + // Send the event immediately + XFlush(display); + XUnlockDisplay(display); + + self->_expiry += self->interval ? self->interval : UI_INTERVAL; + } + } + + Py_DECREF(self->context_arg); + + PyGILState_Release(gstate); +} + + +// ---------------------------------------------------------------------------- +static void +BaseCanvas__transform_coordinates(BaseCanvas * self, int * x, int * y) { + Display * display = Atelier_get_display(); + int screen = DefaultScreen(display); + XineramaScreenInfo * si = Atelier_get_screen_info(self->xine_screen); + + int width = si ? si->width : XDisplayWidth(display, screen); + int height = si ? si->height : XDisplayHeight(display, screen); + int x_org = si ? si->x_org : 0; + int y_org = si ? si->y_org : 0; + + if ((self->gravity - 1) % 3 == 0) *x = self->x; + else if ((self->gravity - 2) % 3 == 0) *x = ((width - self->width) >> 1) + self->x; + else *x = width - self->width - self->x; + *x += x_org; + + if (self->gravity <= 3) *y = self->y; + else if (self->gravity <= 6) *y = ((height - self->height) >> 1) + self->y; + else *y = height - self->height - self->y; + *y += y_org; +} + + +// +// class BaseCanvas: +// + +// +// def __del__(self): +// +static void +BaseCanvas_dealloc(BaseCanvas* self) { + Py_TYPE(self)->tp_free((PyObject*)self); +} + + +// +// def __new__(self, *args, **kwargs): +// +static PyObject * +BaseCanvas_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { + BaseCanvas * self; + + self = (BaseCanvas *)type->tp_alloc(type, 0); + if (self != NULL) { + char * keywords[] = {"x", "y", "width", "height", + "interval", // 1000 + "screen", // 0 + "window_type", // CanvasType.DOCK + "gravity", // CanvasGravity.NORTH_WEST + "sticky", // True + "keep_below" // True + "skip_taskbar", // True + "skip_pager", // True + NULL + }; + + // Default keyword arguments + self->interval = 1000; + self->xine_screen = 0; + int window_type = 2; // BaseCanvasType.DOCK + self->gravity = 1; // BaseCanvasGravity.NORTH_WEST + int sticky = 1; + int keep_below = 1; + int skip_taskbar = 1; + int skip_pager = 1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "IIII|IIIIpppp:BaseCanvas.__new__", + keywords, + &self->x, &self->y, &self->width, &self->height, + &self->interval, + &self->xine_screen, + &window_type, + &self->gravity, + &sticky, + &keep_below, + &skip_taskbar, + &skip_pager + ) + ) return NULL; + + Display * display = Atelier_get_display(); + if (display == NULL) + return NULL; + + int screen = DefaultScreen(display); + + // Query Visual for "TrueColor" and 32 bits depth (RGBA) + XMatchVisualInfo(display, screen, 32, TrueColor, &visualinfo); + attr.colormap = XCreateColormap(display, DefaultRootWindow(display), visualinfo.visual, AllocNone); + attr.border_pixel = 0; + attr.background_pixel = 0; + + int x, y; + BaseCanvas__transform_coordinates(self, &x, &y); + + self->win_id = XCreateWindow( + display, + DefaultRootWindow(display), + x, + y, + self->width, + self->height, + 0, + visualinfo.depth, + InputOutput, + visualinfo.visual, + CWColormap | CWBorderPixel | CWBackPixel | CWWinGravity, + &attr + ); + + BaseCanvas__change_property(self, "_NET_WM_WINDOW_TYPE", WINDOW_TYPE_MAP[window_type], PropModeReplace); + + if (keep_below != 0) BaseCanvas__change_property(self, "_NET_WM_STATE", "_NET_WM_STATE_BELOW" , PropModeAppend); + if (sticky != 0) BaseCanvas__change_property(self, "_NET_WM_STATE", "_NET_WM_STATE_STICKY" , PropModeAppend); + if (skip_taskbar != 0) BaseCanvas__change_property(self, "_NET_WM_STATE", "_NET_WM_STATE_SKIP_TASKBAR" , PropModeAppend); + if (skip_pager != 0) BaseCanvas__change_property(self, "_NET_WM_STATE", "_NET_WM_STATE_SKIP_PAGER" , PropModeAppend); + + XCreateGC(display, self->win_id, 0, 0); + + // Handle Delete Event + self->wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(display, self->win_id, (Atom *) &(self->wm_delete_window), 1); + + // Create the Cairo Context + self->surface = cairo_xlib_surface_create( + display, + self->win_id, + visualinfo.visual, + self->width, + self->height + ); + cairo_xlib_surface_set_size(self->surface, self->width, self->height); + self->context = cairo_create(self->surface); + + self->_running = 0; + self->_drawing = 0; + self->_needs_redraw = 0; + + // Register the BaseCanvas with the Atelier + Atelier_add_canvas(self); + } + + return (PyObject *)self; +} + + +// +// def __init__(self, *args, **kwargs): +// +static int +BaseCanvas_init(BaseCanvas *self, PyObject *args, PyObject *kwds) +{ + return 0; +} + + +// +// def move(self, x, y): +// """Move the canvas to new coordinates relative to the current gravity. +// """ +// +static PyObject * +BaseCanvas_move(BaseCanvas * self, PyObject * args, PyObject * kwargs) { + int new_x, new_y; + char * keywords[] = {"x", "y", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "II:BaseCanvas.move", + keywords, &new_x, &new_y) + ) return NULL; + + int x, y; + self->x = new_x; + self->y = new_y; + BaseCanvas__transform_coordinates(self, &x, &y); + + XMoveWindow(Atelier_get_display(), self->win_id, x, y); + + Py_INCREF(Py_None); return Py_None; +} + + +// +// def show(self): +// """Show the canvas. +// """ +// +static PyObject * +BaseCanvas_show(BaseCanvas* self) { + Display * display = Atelier_get_display(); + + // Input events + XSelectInput(display, self->win_id, + ButtonPressMask + | KeyPressMask + | ExposureMask + ); + XMapWindow(display, self->win_id); + + self->_running = 1; + + // Use the allocated BaseCanvas object to pass arguments to the UI thread. + PyThread_start_new_thread((void (*)(void *)) BaseCanvas__ui_thread, self); + + Py_INCREF(Py_None); return Py_None; +} + + +// +// def get_size(self): +// """Get the size of the BaseCanvas. +// +// Return: +// (tuple) The `(width, height)` tuple. +// """ +// +static PyObject * +BaseCanvas_get_size(BaseCanvas * self) { + return Py_BuildValue("(ii)", self->width, self->height); +} + + +// +// def dispose(self): +// """Dispose of the canvas when no longer needed. +// This method marks the canvas it is called on as ready to be destroyed. +// The actual destruction is performed by the event loop, which calls the +// `destroy` method. This is the thread-safe way of destrying an X11 +// BaseCanvas object. +// """ +// +static PyObject * +BaseCanvas_dispose(BaseCanvas * self) { + Display * display = Atelier_get_display(); + + XUnmapWindow(display, self->win_id); + + XEvent event; + event.type = ClientMessage; + event.xany.window = self->win_id; + event.xclient.format = 32; + event.xclient.data.l[0] = self->wm_delete_window; + + XLockDisplay(display); + XSendEvent(display, self->win_id, True, 0, &event); + // Send the event immediately + XFlush(display); + XUnlockDisplay(display); + + Py_INCREF(Py_None); return Py_None; +} + + +// +// def destroy(self): +// """Destroy the canvas. +// WARNING: Not thread-safe. Use `dispose` instead. +// """ +// +static PyObject * +BaseCanvas_destroy(BaseCanvas * self) { + self->_running = 0; + cairo_destroy(self->context); + cairo_surface_destroy(self->surface); + + // De-register BaseCanvas from Atelier; + Atelier_remove_canvas(self); + + Py_INCREF(Py_None); return Py_None; +} diff --git a/uwidgets/uwidgets/x11/base_canvas.h b/uwidgets/uwidgets/x11/base_canvas.h new file mode 100644 index 000000000..14398bca1 --- /dev/null +++ b/uwidgets/uwidgets/x11/base_canvas.h @@ -0,0 +1,163 @@ +// This file is part of "uwidgets" which is released under GPL. +// +// See file LICENCE or go to http://www.gnu.org/licenses/ for full license +// details. +// +// uwidgets is a desktop widget creation and management library for Python 3. +// +// Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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/>. + +#ifndef BASE_CANVAS_H +#define BASE_CANVAS_H + +#include <Python.h> +#include "structmember.h" + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <cairo.h> +#include <cairo-xlib.h> + + +typedef struct { + PyObject_HEAD + // Geometry + int x; + int y; + int width; + int height; + + // X/Cairo data structures + cairo_t * context; + PyObject * context_arg; + cairo_surface_t * surface; + Display * display; + int screen; + Drawable win_id; + + // Signals + Atom wm_delete_window; + + // Attributes + unsigned int interval; + unsigned int xine_screen; + int gravity; + + // Internal attributes + int _running; + long _expiry; + int _drawing; + int _needs_redraw; +} BaseCanvas; + +void BaseCanvas__redraw(BaseCanvas * self); + +#ifdef BASE_CANVAS_C +// ---- METHODS ---- +static void BaseCanvas_dealloc (BaseCanvas *); +static PyObject * BaseCanvas_new (PyTypeObject *, PyObject *, PyObject *); +static int BaseCanvas_init (BaseCanvas *, PyObject *, PyObject *); + +static PyObject * BaseCanvas_move (BaseCanvas *, PyObject *, PyObject *); +static PyObject * BaseCanvas_show (BaseCanvas *); +static PyObject * BaseCanvas_get_size (BaseCanvas *); +static PyObject * BaseCanvas_dispose (BaseCanvas *); +static PyObject * BaseCanvas_destroy (BaseCanvas *); + + +static PyMethodDef BaseCanvas_methods[] = { + {"move" , (PyCFunction) BaseCanvas_move , METH_VARARGS | METH_KEYWORDS, + "Move the canvas to new coordinates.\n\n" + + "The *x* and *y* coordinates are relative to the canvas gravity." + }, + {"show" , (PyCFunction) BaseCanvas_show , METH_NOARGS, + "Map the canvas to screen and set it ready for drawing." + }, + {"get_size" , (PyCFunction) BaseCanvas_get_size , METH_NOARGS, + "Get the canvas size.\n\n" + + "Returns:\n" + " tuple: the 2-tuple of width and height in pixels." + }, + {"dispose" , (PyCFunction) BaseCanvas_dispose , METH_NOARGS, + "Mark the canvas as ready to be destroyed to free up resources." + }, + {"destroy" , (PyCFunction) BaseCanvas_destroy , METH_NOARGS, + "Destroy the canvas.\n\n" + + "This method is not thread-safe. Use the :func:`dispose` method instead." + }, + {NULL} /* Sentinel */ +}; + + +// ---- ATTRIBUTES ---- +static PyMemberDef BaseCanvas_members[] = { + {"interval" , T_INT , offsetof(BaseCanvas, interval) , 0 , "The refresh interval, in milliseconds."}, + {"x" , T_INT , offsetof(BaseCanvas, x) , READONLY , "The canvas *x* coordinate. *Read-only*."}, + {"y" , T_INT , offsetof(BaseCanvas, y) , READONLY , "The canvas *y* coordinate. *Read-only*."}, + {"width" , T_INT , offsetof(BaseCanvas, width) , READONLY , "The canvas width. *Read-only*."}, + {"height" , T_INT , offsetof(BaseCanvas, height) , READONLY , "The canvas height. *Read-only*."}, + {NULL} /* Sentinel */ +}; + +// ---- OBJECT TYPE DECLARATION ---- +PyTypeObject BaseCanvasType = { + PyVarObject_HEAD_INIT(NULL, 0) + "x11.BaseCanvas", /* tp_name */ + sizeof(BaseCanvas), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)BaseCanvas_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + "BaseCanvas objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + BaseCanvas_methods, /* tp_methods */ + BaseCanvas_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)BaseCanvas_init, /* tp_init */ + 0, /* tp_alloc */ + BaseCanvas_new, /* tp_new */ +}; +#endif + +#endif diff --git a/uwidgets/uwidgets/x11/canvas.py b/uwidgets/uwidgets/x11/canvas.py new file mode 100644 index 000000000..17e2d5e9e --- /dev/null +++ b/uwidgets/uwidgets/x11/canvas.py @@ -0,0 +1,407 @@ +# This file is part of "uwidgets" which is released under GPL. +# +# See file LICENCE or go to http://www.gnu.org/licenses/ for full license +# details. +# +# uwidgets is a desktop widget creation and management library for Python 3. +# +# Copyright (c) 2018 Gabriele N. Tornetta <phoenix1987@gmail.com>. +# All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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/>. + +""" +Description +=========== + +This module provides the :class:`Canvas` class for the creation of X11 +canvases. + +The :class:`Canvas` class is, in Java terminoly, *abstract* and should not be +instantiated directly. Instead, applications should define their own subclasses +of the :class:`Canvas` and implement the :func:`on_draw` method, which gets +called periodically to perform the required draw operations using pycairo. + +Once created, an instance of a subclass of :class:`Canvas` can be shown on +screen by calling the :func:`show` method. This starts drawing the canvas on +screen by calling the `on_draw` callback at regular intervals in time. Events +can be handled by starting the event loop with +:func:`uwidgets.x11.start_event_loop`, as described in more details in the +`Event handling`_ section. + + +Creating a canvas +----------------- + +Canvases are created by simply subclassing the `Canvas` class and implementing +the :func:`on_draw` callback. + +The :class:`Canvas` constructor (i.e. the :func:`__new__` magic method) takes +the following arguments: + ++----------------+------------------------------------------------------------+ +| Argument | Description | ++================+========================+===================================+ +| *x* | These arguments describe the basic geometry of the canvas. | ++----------------+ The *x* and *y* coordinates are relative to the ``gravity``| +| *y* | argument (see below). The *width* and *height* arguments | ++----------------+ give the canvas size in pixels. | +| *width* | | ++----------------+ | +| *height* | | ++----------------+------------------------------------------------------------+ +| *interval* | The time interval between calls to the :func:`on_draw` | +| | callback, in `milliseconds`. | +| | | +| | **Default value**: 1000 (i.e. 1 second) | ++----------------+------------------------------------------------------------+ +| *screen* | In multi-screen setups, specifies on which screen the | +| | canvas is to be drawn. The value 0 identifies the first | +| | screen. To treat the physical screens as a single virtual | +| | screen, use the value -1. | +| | | +| | **Default value**: 0 (i.e. the first screen) | ++----------------+------------------------------------------------------------+ +| *window_type* | The type of window to create. The possible choices are | +| | enumerated in the ``uwidgets.CanvasType`` type and are | +| | named after the equivalent _NET_WM_WINDOW_TYPE hints for | +| | the window manager. This is analogous to conky's | +| | ``own_window_type`` configuration setting. | +| | | +| | **Default value**: ``CanvasType.DOCK`` | ++----------------+------------------------------------------------------------+ +| *gravity* | Defines the coordinate system for the canvas relative to | +| | the screen. The allowed values are enumerated in the | +| | ``uwidgets.CanvasGravity`` type. This is the equivalent of | +| | the conky ``alignment`` configuration setting. For example,| +| | the value ``CanvasGravity.SOUTH_EAST`` indicates that the | +| | canvas should be positioned relative to the bottom-right | +| | corner of the screen. | +| | | +| | **Default value**: ``CanvasGravity.NORTH_WEST`` | ++----------------+------------------------------------------------------------+ +| *sticky* | Whether the window should *stick* to the desktop and hence | +| | be visible in all workspaces. | +| | | +| | **Default value**: ``True`` | ++----------------+------------------------------------------------------------+ +| *keep_below* | Whether the window should stay below any other window on | +| | the screen. | +| | | +| | **Default value**: ``True`` | ++----------------+------------------------------------------------------------+ +| *skip_taskbar* | Whether the window should not have an entry in the taskbar.| +| | | +| | **Default value**: ``True`` | ++----------------+------------------------------------------------------------+ +| *skip_pager* | Whether the window should not appear in the pager. | +| | | +| | **Default value**: ``True`` | ++----------------+------------------------------------------------------------+ + +Note that the interval can be changed dynamically by setting the ``interval`` +attribute on the canvas object directly after it has been created. + +If you want to distribute your subclasses of :class:`Canvas`, we recommend that +you create a static method ``build`` that returns an instance of the subclass, +with some of the argumets set to a predefined values. This is useful if you +want to distribute widgets with, e.g., a predefined size, as a Python module. + +Showing the canvas +------------------ + +When a canvas is created, it is not immediately shown to screen. To map it to +screen and start the draw cycle one has to call the :func:`show` method +explicitly. + +If you need to pass data to the canvas, you might want to do that before +calling this method, since presumably the :func:`on_draw` callback, which will +start to be called, makes use of it. + +Finally, you must start the main event loop with +:func:`uwidgets.x11.start_event_loop` to start drawing on the canvases, and in +case that they should handle input events, like mouse button clicks or key +presses. Note however that execution in the current thread will halt at this +call, until it returns after a call to :func:`uwidgets.x11.stop_event_loop`. + +For more details on how to handle events with your X11 canvases, see the +section `Event handling`_ below. + + +Disposing of a canvas +--------------------- + +If you want to programmatically dispose of a canvas, you can call the +:func:`dispose` method. This doesn't destroy the canvas immediately, but sends +a delete request to the main event loop instead. This is the preffered way of +getting rid of a canvas when you are running the event loop. You can also use +the :func:`destroy` method directly, which destroys the canvas immediately. +However this is not thread safe and should not be called in the :func:`on_draw` +callback when running the event loop. + + +Event handling +-------------- + +A feature that distinguishes uwidgets from conky is that it allows you to handle +simple user input on the canvases. Currently, X11 canvases support two events: +mouse button and key press events. + +Mouse button events can be handled by implementing the +:func:`on_button_pressed` callback in the subclass of :class:`Canvas`. The +signature is the following:: + + def on_button_pressed(self, button, state, x, y): + +and the semantics of the arguments is the same as the ``XButtonEvent`` [1]_. + +To handle key presses, implement the ``on_key_pressed`` callback with the +following signature:: + + def on_key_pressed(self, keysym, state): + +The ``state`` argument has the same semantics as in the +:func:`on_button_pressed` case, while the ``keysym`` is described, e,g, in the +`Keyboard Econding +<https://tronche.com/gui/x/xlib/input/keyboard-encoding.html>`_ section of the +Xlib guide. + +A simple example +---------------- + +Here is a simple example that shows all the above concepts in action:: + + from uwidgets import CanvasGravity + from uwidgets.x11 import Canvas, start_event_loop + + class MyCanvas(Canvas): + @staticmethod + def build(x, y): + return MyCanvas(x, y, 200, 200, gravity = CanvasGravity.NORTH) + + def on_button_pressed(self, button, state, x, y): + if button == 1: # Left mouse button pressed + self.dispose() + + def on_draw(self, ctx): + ctx.set_source_rgb(1, 0, 0) + ctx.rectangle(0, 0, ctx.canvas.width >> 1, ctx.canvas.height >> 1) + ctx.fill() + + if __name__ == "__main__": + # Instantiate the canvas + canvas = MyCanvas.build() + + # Map it on screen + canvas.show() + + # Start the event loop + start_event_loop() + + +Extra features +============== + +The :class:`Canvas` class comes with some handy extra features that can help +with common patterns, thus sparing you to have to type boilerplate code. + +Brushes +------- + +Brushes are a way to rebind methods from your subclass of :class:`Canvas` to +the Cairo context. Consider the following example:: + + from random import random as r + + class RectCanvas(uwidgets.x11.Canvas): + def rect(self, ctx, width, height): + ctx.set_source_rgb(*[r() for _ in range(3)]) + ctx.rectangle(0, 0, width, height) + ctx.fill() + + def on_draw(self, ctx): + for i in range(4): + self.rect(ctx, self.width >> i, self.height >> i) + +The method ``rect`` is defined under the class ``RectCanvas`` for convenience. +However, from a logical point of view, it would make more sense for this method +to belong to ``ctx``, since the general pattern of these helper methods +requires that we pass ``ctx`` as one of the arguments. + +If one prefixes the ``rect`` method with ``draw_`` then it turns into an +*implicit brush*. The :func:`on_draw` callback is called with the ``ctx`` +argument being an instance of ``ExtendedContext``. The ``draw_rect`` brush is +then available from ``ctx`` as a bound method. The sample code above can then +be refactored as:: + + from random import random as r + + class RectCanvas(uwidgets.x11.Canvas): + def draw_rect(ctx, width, height): + ctx.set_source_rgb(*[r() for _ in range(3)]) + ctx.rectangle(0, 0, width, height) + ctx.fill() + + def on_draw(self, ctx): + for i in range(4): + ctx.rect(self.width >> i, self.height >> i) + +Notice how ``draw_rect`` now takes less arguments, and how the first one is +``ctx``, the (extended) Cairo context. + +If you do not wish to prefix your methods with ``draw_``, you can use the +:func:`uwidgets.brush` decorator instead to create an *explicit brush*. The code +would then look like this:: + + from uwidgets import brush + from random import random as r + + class RectCanvas(uwidgets.x11.Canvas): + @brush + def rect(ctx, width, height): + ctx.set_source_rgb(*[r() for _ in range(3)]) + ctx.rectangle(0, 0, width, height) + ctx.fill() + + def on_draw(self, ctx): + for i in range(4): + ctx.rect(self.width >> i, self.height >> i) + + +Text alignment +-------------- + +A common task is writing text on a canvas. With Cairo, text alignment usually +requires the same pattern: get the text extents and compute the new position. +To help with that, :class:`Canvas` objects come with a pre-defined +:func:`write_text` brush. Please refer to the API documentation below for usage +details. + + +Grid +---- + +When designing a canvas from scrach, it is hard to guess at positions without +any guiding lines. To help with precise placement, every :class:`Canvas` object +comes with a ``draw_grid`` brush that creates a rectangular grid on the canvas. +The spacing between the lines is set to 50 pixels by default (assuming that +the scale hasn't been changed before). This can be adjusted by passing the new +spacing along the two directions as arguments. Please refer to the API +documentation below for more details. + + +References +========== + +.. [1] https://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html + + +Module API +========== +""" + +from uwidgets import ExtendedContext, TextAlign, brush +from uwidgets._brush import BrushSets, draw_grid, write_text +from uwidgets._x11 import BaseCanvas + + +class Canvas(BaseCanvas): + """X11 Canvas object. + + This class is meant to be used as a superclass and should not be + instantiated directly. Subclasses should implement the :func:`on_draw` + callback, which is invoked every time the canvas needs to be redrawn. + Redraws happen at regular intervals in time, as specified by the + ``interval`` attribute (also passed as an argument via the constructor). + """ + + def __init__(self, *args, **kwargs): + """Initialise the Canvas object. + + If this method is overriden, keep in mind that the initialisation looks + up for brushes inherited from all the superclasses. It is therefore + important that the method from ``super()`` is called to ensure the + correct functioning of the brushes. + """ + BrushSets.inherit(type(self)) + self._extended_context = None + + def _on_draw(self, ctx): + """Draw callback (internal). + + This is the callback that actually gets called from the BaseCanvas + class. It ensures that an instance of ``ExtendedContext`` is created + before going on to delegate the draw procedures to the user-defined + :func:`on_draw` callback. + + If you want to skip some of the iterations and retain the current + content of the canvas, you can return ``True``. This is useful to avoid + performing the same drawing operations when not required because no + data to display has changed. + """ + if self._extended_context is None: + self._extended_context = ExtendedContext(ctx, self) + + return self.on_draw(self._extended_context) + + def on_draw(self, ctx): + """Draw callback. + + Once the :func:`show` method is called on a :class:`Canvas` object, + this method gets called at regular intervals of time to perform the + draw operation. Every subclass of :class:`Canvas` must implement this + method. + """ + raise NotImplementedError("on_draw method not implemented in subclass.") + + def draw_grid(ctx, x = 50, y = 50): + """Draw a grid on the canvas [**implicit brush**]. + + This implicit brush method is intended to help with determining the + location of points on the canvas during development. + + Args: + x (int): The horizontal spacing between lines. + y (int): The vertical spacing between lines. + """ + draw_grid(ctx, x, y) + + @brush + def write_text(cr, x, y, text, align = TextAlign.TOP_LEFT): + """Write aligned text [**explicit brush**]. + + This explicit brush method helps write aligned text on the canvas. The + *x* and *y* coordinates are relative to the specified *alignment*. By + default, this is ``uwidgets.TextAlign.TOP_LEFT``, meaning that the text + will be left-aligned and on top of the horizontal line that passes + through *y* on the vertical axis. In terms of the point *(x,y)* on the + Canvas, the text will develop in the NE direction. + + The return value is the text extents, in case that some further draw + operations depend on the space required by the text to be drawn on the + canvas. + + Note that font face and size need to be set on the Cairo context prior + to a call to this method. + + Args: + x (int): The horizontal coordinate. + y (int): The vertical coordinate. + text (str): The text to write. + align (int): The text alignment. Detaulf is ``TextAlign.TOP_LEFT``. + + Returns: + tuple: The same return value as ``cairo.text_extents``. + + """ + return write_text(cr, x, y, text, align) diff --git a/uwidgets/uwidgets/x11/pycairo.h b/uwidgets/uwidgets/x11/pycairo.h new file mode 100755 index 000000000..c96526806 --- /dev/null +++ b/uwidgets/uwidgets/x11/pycairo.h @@ -0,0 +1,280 @@ +/* -*- mode: C; c-basic-offset: 2 -*- + * + * Pycairo - Python bindings for cairo + * + * Copyright © 2003 James Henstridge + * Copyright © 2004-2011 Steven Chaplin + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +#ifndef _PYCAIRO_H_ +#define _PYCAIRO_H_ + +#include <Python.h> + +#include <cairo.h> + + +typedef struct { + PyObject_HEAD + cairo_t *ctx; + PyObject *base; /* base object used to create context, or NULL */ +} PycairoContext; + +typedef struct { + PyObject_HEAD + cairo_font_face_t *font_face; +} PycairoFontFace; + +#define PycairoToyFontFace PycairoFontFace + +typedef struct { + PyObject_HEAD + cairo_font_options_t *font_options; +} PycairoFontOptions; + +typedef struct { + PyObject_HEAD + cairo_matrix_t matrix; +} PycairoMatrix; + +typedef struct { + PyObject_HEAD + cairo_path_t *path; +} PycairoPath; + +typedef struct { + PyObject_HEAD + cairo_pattern_t *pattern; + PyObject *base; /* base object used to create pattern, or NULL */ +} PycairoPattern; + +typedef struct { + PyObject_HEAD + cairo_rectangle_int_t rectangle_int; +} PycairoRectangleInt; + +typedef struct { + PyObject_HEAD + cairo_region_t *region; +} PycairoRegion; + +#define PycairoSolidPattern PycairoPattern +#define PycairoSurfacePattern PycairoPattern +#define PycairoGradient PycairoPattern +#define PycairoLinearGradient PycairoPattern +#define PycairoRadialGradient PycairoPattern + +typedef struct { + PyObject_HEAD + cairo_scaled_font_t *scaled_font; +} PycairoScaledFont; + +typedef struct { + PyObject_HEAD + cairo_surface_t *surface; + PyObject *base; /* base object used to create surface, or NULL */ +} PycairoSurface; + +#define PycairoImageSurface PycairoSurface +#define PycairoPDFSurface PycairoSurface +#define PycairoPSSurface PycairoSurface +#define PycairoRecordingSurface PycairoSurface +#define PycairoSVGSurface PycairoSurface +#define PycairoWin32Surface PycairoSurface +#define PycairoWin32PrintingSurface PycairoSurface +#define PycairoXCBSurface PycairoSurface +#define PycairoXlibSurface PycairoSurface + +/* get C object out of the Python wrapper */ +#define PycairoContext_GET(obj) (((PycairoContext *)(obj))->ctx) + +/* Define structure for C API. */ +typedef struct { + /* (type object, constructor) pairs */ + PyTypeObject *Context_Type; + PyObject *(*Context_FromContext)(cairo_t *ctx, PyTypeObject *type, + PyObject *base); + PyTypeObject *FontFace_Type; + PyTypeObject *ToyFontFace_Type; + PyObject *(*FontFace_FromFontFace)(cairo_font_face_t *font_face); + PyTypeObject *FontOptions_Type; + PyObject *(*FontOptions_FromFontOptions)( + cairo_font_options_t *font_options); + PyTypeObject *Matrix_Type; + PyObject *(*Matrix_FromMatrix)(const cairo_matrix_t *matrix); + PyTypeObject *Path_Type; + PyObject *(*Path_FromPath)(cairo_path_t *path); + + PyTypeObject *Pattern_Type; + PyTypeObject *SolidPattern_Type; + PyTypeObject *SurfacePattern_Type; + PyTypeObject *Gradient_Type; + PyTypeObject *LinearGradient_Type; + PyTypeObject *RadialGradient_Type; + PyObject *(*Pattern_FromPattern)(cairo_pattern_t *pattern, PyObject *base); + + PyTypeObject *ScaledFont_Type; + PyObject *(*ScaledFont_FromScaledFont)(cairo_scaled_font_t *scaled_font); + + PyTypeObject *Surface_Type; + PyTypeObject *ImageSurface_Type; + PyTypeObject *PDFSurface_Type; + PyTypeObject *PSSurface_Type; + PyTypeObject *SVGSurface_Type; + PyTypeObject *Win32Surface_Type; + PyTypeObject *Win32PrintingSurface_Type; + PyTypeObject *XCBSurface_Type; + PyTypeObject *XlibSurface_Type; + PyObject *(*Surface_FromSurface)(cairo_surface_t *surface, PyObject *base); + + /* misc functions */ + int (*Check_Status)(cairo_status_t status); + + PyTypeObject *RectangleInt_Type; + PyObject *(*RectangleInt_FromRectangleInt)( + const cairo_rectangle_int_t *rectangle_int); + + PyTypeObject *Region_Type; + PyObject *(*Region_FromRegion)(cairo_region_t *region); + + PyTypeObject *RecordingSurface_Type; +} Pycairo_CAPI_t; + + +#ifndef _INSIDE_PYCAIRO_ + +/* Macros for accessing the C API */ +#define PycairoContext_Type *(Pycairo_CAPI->Context_Type) +#define PycairoContext_FromContext (Pycairo_CAPI->Context_FromContext) +#define PycairoFontFace_Type *(Pycairo_CAPI->FontFace_Type) +#define PycairoToyFontFace_Type *(Pycairo_CAPI->ToyFontFace_Type) +#define PycairoFontFace_FromFontFace (Pycairo_CAPI->FontFace_FromFontFace) +#define PycairoFontOptions_Type *(Pycairo_CAPI->FontOptions_Type) +#define PycairoFontOptions_FromFontOptions \ + (Pycairo_CAPI->FontOptions_FromFontOptions) +#define PycairoMatrix_Type *(Pycairo_CAPI->Matrix_Type) +#define PycairoMatrix_FromMatrix (Pycairo_CAPI->Matrix_FromMatrix) +#define PycairoPath_Type *(Pycairo_CAPI->Path_Type) +#define PycairoPath_FromPath (Pycairo_CAPI->Path_FromPath) + +#define PycairoPattern_Type *(Pycairo_CAPI->Pattern_Type) +#define PycairoSolidPattern_Type *(Pycairo_CAPI->SolidPattern_Type) +#define PycairoSurfacePattern_Type *(Pycairo_CAPI->SurfacePattern_Type) +#define PycairoGradient_Type *(Pycairo_CAPI->Gradient_Type) +#define PycairoLinearGradient_Type *(Pycairo_CAPI->LinearGradient_Type) +#define PycairoRadialGradient_Type *(Pycairo_CAPI->RadialGradient_Type) +#define PycairoPattern_FromPattern (Pycairo_CAPI->Pattern_FromPattern) + +#define PycairoRectangleInt_Type *(Pycairo_CAPI->RectangleInt_Type) +#define PycairoRectangleInt_FromRectangleInt \ + (Pycairo_CAPI->RectangleInt_FromRectangleInt) + +#define PycairoRegion_Type *(Pycairo_CAPI->Region_Type) +#define PycairoRegion_FromRegion (Pycairo_CAPI->Region_FromRegion) + +#define PycairoScaledFont_Type *(Pycairo_CAPI->ScaledFont_Type) +#define PycairoScaledFont_FromScaledFont \ + (Pycairo_CAPI->ScaledFont_FromScaledFont) + +#define PycairoSurface_Type *(Pycairo_CAPI->Surface_Type) +#define PycairoImageSurface_Type *(Pycairo_CAPI->ImageSurface_Type) + +#ifdef CAIRO_HAS_PDF_SURFACE +#define PycairoPDFSurface_Type *(Pycairo_CAPI->PDFSurface_Type) +#endif + +#ifdef CAIRO_HAS_PS_SURFACE +#define PycairoPSSurface_Type *(Pycairo_CAPI->PSSurface_Type) +#endif + +#ifdef CAIRO_HAS_RECORDING_SURFACE +#define PycairoRecordingSurface_Type \ + *(Pycairo_CAPI->RecordingSurface_Type) +#endif + +#ifdef CAIRO_HAS_SVG_SURFACE +#define PycairoSVGSurface_Type *(Pycairo_CAPI->SVGSurface_Type) +#endif + +#ifdef CAIRO_HAS_WIN32_SURFACE +#define PycairoWin32Surface_Type *(Pycairo_CAPI->Win32Surface_Type) +#define PycairoWin32PrintingSurface_Type \ + *(Pycairo_CAPI->Win32PrintingSurface_Type) +#endif + +#ifdef CAIRO_HAS_XCB_SURFACE +#define PycairoXCBSurface_Type *(Pycairo_CAPI->XCBSurface_Type) +#endif + +#ifdef CAIRO_HAS_XLIB_SURFACE +#define PycairoXlibSurface_Type *(Pycairo_CAPI->XlibSurface_Type) +#endif + +#define PycairoSurface_FromSurface (Pycairo_CAPI->Surface_FromSurface) + +#define Pycairo_Check_Status (Pycairo_CAPI->Check_Status) + +#if PY_MAJOR_VERSION < 3 + +/* To access the Pycairo C API, edit the client module file to: + * 1) Add the following line to define a global variable for the C API + * static Pycairo_CAPI_t *Pycairo_CAPI; + * 2) Add 'Pycairo_IMPORT;' to the init<module> function + */ +#define Pycairo_IMPORT \ + Pycairo_CAPI = (Pycairo_CAPI_t*) PyCObject_Import("cairo", "CAPI") + +#else + +#ifdef PYCAIRO_NO_IMPORT + +extern Pycairo_CAPI_t *Pycairo_CAPI; + +#else + +/* To access the Pycairo C API, the client module should call 'import_cairo()' + * from the init<module> function, and check the return value, < 0 means the + * import failed. + */ +Pycairo_CAPI_t *Pycairo_CAPI; + +/* Return -1 on error, 0 on success. + * PyCapsule_Import will set an exception if there's an error. + */ +static int +import_cairo(void) +{ + Pycairo_CAPI = (Pycairo_CAPI_t*) PyCapsule_Import("cairo.CAPI", 0); + return (Pycairo_CAPI != 0) ? 0 : -1; +} + +#endif + +#endif + +#endif /* ifndef _INSIDE_PYCAIRO_ */ + +#endif /* ifndef _PYCAIRO_H_ */ diff --git a/uwidgets/widget.ini b/uwidgets/widget.ini new file mode 100644 index 000000000..8c0d94f78 --- /dev/null +++ b/uwidgets/widget.ini @@ -0,0 +1,4 @@ +[widget] +name=Unsplash Background +summary=A widget to set a random Unsplash wallpaper for the Unity desktop. +exec=python3 unsplash-background.py \ No newline at end of file |
