diff options
| author | Daniel van Vugt <vanvugt@gmail.com> | 2012-02-07 15:42:12 +0800 |
|---|---|---|
| committer | Daniel van Vugt <vanvugt@gmail.com> | 2012-02-07 15:42:12 +0800 |
| commit | 2d1db98b35885672f7602142e87431f437aedd3f (patch) | |
| tree | 2c0f364c5865958947787db7f89f8fcfaa95afda | |
| parent | b4d7573a88ea2c532938e65f4b8ae42bb4b287e2 (diff) | |
| parent | ff8f505e2eeb3d7c32f8cb7126b6b98156e1e754 (diff) | |
Rebase.
(bzr r1827.1.3)
90 files changed, 5191 insertions, 783 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 14206c816..2c3998808 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,7 +113,7 @@ if (BOOT_LOGGER) SET (BOOT_LOGGER_FLAG "-DENABLE_LOGGER") endif (BOOT_LOGGER) -SET (MAINTAINER_CFLAGS "-Werror -Wall -Wcast-align -Wno-uninitialized -Wempty-body -Wformat-security -Winit-self -Warray-bounds") +SET (MAINTAINER_CFLAGS "-Wall -Wcast-align -Wno-uninitialized -Wempty-body -Wformat-security -Winit-self -Warray-bounds") option (DISABLE_MAINTAINER_CFLAGS "Disable maintainer CFlags" OFF) if (DISABLE_MAINTAINER_CFLAGS) SET (MAINTAINER_CFLAGS "") diff --git a/UnityCore/CMakeLists.txt b/UnityCore/CMakeLists.txt index 3b0d5140b..0242eecab 100644 --- a/UnityCore/CMakeLists.txt +++ b/UnityCore/CMakeLists.txt @@ -21,6 +21,7 @@ set (CORE_HEADERS GLibSignal-inl.h GLibWrapper.h GLibWrapper-inl.h + Hud.h HomeLens.h IndicatorEntry.h Indicator.h @@ -54,6 +55,7 @@ set (CORE_SOURCES GLibDBusProxy.cpp GLibSignal.cpp GLibWrapper.cpp + Hud.cpp HomeLens.cpp Indicator.cpp IndicatorEntry.cpp diff --git a/UnityCore/GLibDBusProxy.cpp b/UnityCore/GLibDBusProxy.cpp index 1cafe872c..bf77b0b08 100644 --- a/UnityCore/GLibDBusProxy.cpp +++ b/UnityCore/GLibDBusProxy.cpp @@ -141,11 +141,7 @@ void DBusProxy::Impl::OnNameAppeared(GDBusConnection* connection, gpointer impl) { DBusProxy::Impl* self = static_cast<DBusProxy::Impl*>(impl); - LOG_DEBUG(logger) << self->name_ << " appeared"; - - self->connected_ = true; - self->owner_->connected.emit(); } void DBusProxy::Impl::OnNameVanished(GDBusConnection* connection, diff --git a/UnityCore/Hud.cpp b/UnityCore/Hud.cpp new file mode 100644 index 000000000..d938d80a3 --- /dev/null +++ b/UnityCore/Hud.cpp @@ -0,0 +1,255 @@ +// -*- 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: Gordon Allott <gord.allott@canonical.com> + */ +// +#include "Hud.h" + +#include <gio/gio.h> +#include <glib.h> +#include <NuxCore/Logger.h> +#include "GLibWrapper.h" +#include "GLibDBusProxy.h" + +#include "config.h" + +#include <sigc++/bind.h> + +namespace unity +{ +namespace hud +{ + +namespace +{ +nux::logging::Logger logger("unity.hud.hud"); +const int request_number_of_results = 6; +} + +// Impl classes +class HudImpl +{ +public: + HudImpl(std::string const& dbus_name, + std::string const& dbus_path, + Hud *parent) + : query_key_(NULL) + , proxy_(dbus_name, dbus_path, "com.canonical.hud") + , parent_(parent) + { + LOG_DEBUG(logger) << "Hud init with name: " << dbus_name << "and path: " << dbus_path; + proxy_.connected.connect([&]() { + LOG_DEBUG(logger) << "Hud Connected"; + parent_->connected = true; + }); + + proxy_.Connect("UpdatedQuery", sigc::mem_fun(this, &HudImpl::UpdateQueryCallback)); + } + + void QueryCallback(GVariant* data); + void UpdateQueryCallback(GVariant* data); + void BuildQueries(GVariant* query_array); + void ExecuteByKey(GVariant* key, unsigned int timestamp); + void ExecuteQueryByStringCallback(GVariant* query, unsigned int timestamp); + void CloseQuery(); + + GVariant* query_key_; + Hud::Queries queries_; + glib::DBusProxy proxy_; + Hud* parent_; +}; + +void HudImpl::ExecuteByKey(GVariant* key, unsigned int timestamp) +{ + LOG_DEBUG(logger) << "Executing by Key"; + + GVariantBuilder tuple; + g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&tuple, g_variant_new_variant(key)); + g_variant_builder_add_value(&tuple, g_variant_new_uint32(timestamp)); + + proxy_.Call("ExecuteQuery", g_variant_builder_end(&tuple)); +} + +void HudImpl::ExecuteQueryByStringCallback(GVariant* query, unsigned int timestamp) +{ + if (g_variant_n_children(query) < 3) + { + LOG_ERROR(logger) << "Received (" << g_variant_n_children(query) << ") children in a query, expected 3"; + return; + } + + queries_.clear(); + + GVariant* query_key = g_variant_get_child_value(query, 2); + query_key_ = query_key; + + GVariant* queries = g_variant_get_child_value(query, 1); + BuildQueries(queries); + g_variant_unref(queries); + + if (queries_.empty() == false) + { + // we now execute based off the first result + ExecuteByKey(queries_.front()->key, timestamp); + CloseQuery(); + } +} + +void HudImpl::QueryCallback(GVariant* query) +{ + if (g_variant_n_children(query) < 3) + { + LOG_ERROR(logger) << "Received (" << g_variant_n_children(query) << ") children in a query, expected 3"; + return; + } + queries_.clear(); + + // extract the information from the GVariants + GVariant* target = g_variant_get_child_value(query, 0); + g_variant_unref(target); + + GVariant* query_key = g_variant_get_child_value(query, 2); + query_key_ = query_key; + + GVariant* queries = g_variant_get_child_value(query, 1); + BuildQueries(queries); + g_variant_unref(queries); + + parent_->queries_updated.emit(queries_); +} + +void HudImpl::UpdateQueryCallback(GVariant* query) +{ + if (g_variant_n_children(query) < 3) + { + LOG_ERROR(logger) << "Received (" << g_variant_n_children(query) << ") children in a query, expected 3"; + return; + } + // as we are expecting an update, we want to check + // and make sure that we are the actual receivers of + // the signal + + GVariant* query_key = g_variant_get_child_value(query, 2); + if (g_variant_equal(query_key_, query_key)) + { + GVariant* queries = g_variant_get_child_value(query, 1); + BuildQueries(queries); + g_variant_unref(queries); + } +} + +void HudImpl::BuildQueries(GVariant* query_array) +{ + GVariantIter iter; + g_variant_iter_init(&iter, query_array); + glib::String formatted_text; + glib::String icon; + glib::String item_icon; + glib::String completion_text; + glib::String shortcut; + GVariant* key = NULL; + + while (g_variant_iter_loop(&iter, "(sssssv)", + &formatted_text, &icon, &item_icon, &completion_text, &shortcut, &key)) + { + queries_.push_back(Query::Ptr(new Query(formatted_text, + icon, + item_icon, + completion_text, + shortcut, + key))); + } +} + +void HudImpl::CloseQuery() +{ + if (query_key_ == NULL) + { + LOG_WARN(logger) << "Attempted to close the hud connection without starting it"; + } + else + { + GVariant* paramaters = g_variant_new("(v)", query_key_); + proxy_.Call("CloseQuery", paramaters); + g_variant_unref(query_key_); + query_key_ = NULL; + queries_.clear(); + } +} + + +Hud::Hud(std::string const& dbus_name, + std::string const& dbus_path) + : connected(false) + , pimpl_(new HudImpl(dbus_name, dbus_path, this)) +{ + pimpl_->parent_ = this; +} + +Hud::~Hud() +{ + delete pimpl_; +} + +void Hud::RequestQuery(std::string const& search_string) +{ + LOG_DEBUG(logger) << "Getting Query: " << search_string; + if (pimpl_->query_key_ != NULL) + { + CloseQuery(); + } + + GVariant* paramaters = g_variant_new("(si)", + search_string.c_str(), + request_number_of_results); + pimpl_->proxy_.Call("StartQuery", paramaters, sigc::mem_fun(this->pimpl_, &HudImpl::QueryCallback)); +} + + +void Hud::ExecuteQuery(Query::Ptr query, unsigned int timestamp) +{ + LOG_DEBUG(logger) << "Executing query: " << query->formatted_text; + pimpl_->ExecuteByKey(query->key, timestamp); +} + +void Hud::ExecuteQueryBySearch(std::string execute_string, unsigned int timestamp) +{ + //Does a search then executes the result based on that search + LOG_DEBUG(logger) << "Executing by string" << execute_string; + if (pimpl_->query_key_ != NULL) + { + CloseQuery(); + } + + GVariant* paramaters = g_variant_new("(si)", + execute_string.c_str(), + 1); + + auto functor = sigc::mem_fun(this->pimpl_, &HudImpl::ExecuteQueryByStringCallback); + + pimpl_->proxy_.Call("StartQuery", paramaters, sigc::bind(functor, timestamp)); +} + +void Hud::CloseQuery() +{ + //Send close hint to the hud + pimpl_->CloseQuery(); +} + +} +} diff --git a/UnityCore/Hud.h b/UnityCore/Hud.h new file mode 100644 index 000000000..01acdf666 --- /dev/null +++ b/UnityCore/Hud.h @@ -0,0 +1,127 @@ +// -*- 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: Gordon Allott <gord.allott@canonical.com> + */ + +#ifndef UNITY_HUD_H +#define UNITY_HUD_H + +#include <deque> +#include <string> +#include <memory> +#include <NuxCore/Property.h> +#include <glib/gvariant.h> + +namespace unity +{ +namespace hud +{ + + +class Query +{ +public: + typedef std::shared_ptr<Query> Ptr; + + Query(std::string const& formatted_text_, std::string const& icon_name_, + std::string const& item_icon_, std::string const& completion_text_, + std::string const& shortcut_, GVariant* key_) + : formatted_text(formatted_text_) + , icon_name(icon_name_) + , item_icon(item_icon_) + , completion_text(completion_text_) + , shortcut(shortcut_) + , key(key_) + { + g_variant_ref(key); + } + + ~Query() + { + g_variant_unref(key); + } + + Query(const Query &rhs); + Query& operator=(Query); + + std::string formatted_text; // Pango formatted text + std::string icon_name; // icon name using standard lookups + std::string item_icon; // Future API + std::string completion_text; // Non formatted text f or completion + std::string shortcut; // Shortcut key + GVariant *key; +}; + + +class HudImpl; +class Hud +{ +public: + typedef std::shared_ptr<Hud> Ptr; + typedef std::deque<Query::Ptr> Queries; + + /* + * Constructor for the hud + * \param dbus_name string that specifies the name of the hud service + * \param dbus_path string that specifies the path of the hud service + */ + Hud(std::string const& dbus_name, + std::string const& dbus_path); + + ~Hud(); + + Hud(const Hud &rhs); + Hud& operator=(Hud); + + nux::Property<std::string> target; + nux::Property<bool> connected; + + /* + * Queries the service for new suggestions, will fire off the + * suggestion_search_finished signal when the suggestions are returned + */ + void RequestQuery(std::string const& search_string); + + /* + * Executes a Query + */ + void ExecuteQuery(Query::Ptr query, unsigned int timestamp); + + /* + * Executes a query that returns from a search, + * Implicitly calls CloseQuery(); + */ + void ExecuteQueryBySearch(std::string execute_string, unsigned int timestamp); + + /* + * Closes the query connection, call when the hud closes + */ + void CloseQuery(); + + /* + * Returns a deque of Query types when the service provides them + */ + sigc::signal<void, Queries> queries_updated; + +private: + HudImpl *pimpl_; +}; + +} +} + +#endif /* UNITY_HUD_H */ diff --git a/manual-tests/Hud.txt b/manual-tests/Hud.txt new file mode 100644 index 000000000..9975bee38 --- /dev/null +++ b/manual-tests/Hud.txt @@ -0,0 +1,57 @@ +For reference, the term Tap means to press the indicated key and release it, within a timeframe of 50ms + +Hud Invocate +----------- +This test makes sure that the hud presents itself, the launcher hides +and the panel changes + +#. Tap Alt + +Outcome + The hud interface presents itself with no other interface such as the dash present. + The Launcher hides, the panel becomes transparent with a colour tint matching the hud + +Hud Search +----------- +This test makes sure that the hud will search and activate items. + +#. Ensure the devices indicator is present +#. Tap Alt +#. Type "system" to search for the "System settings..." item in the devices indicator +#. Press return to activate the search query. + +Outcome + The system settings interface presents itself, the hud disappears. + If the hud does not disappear, this test failed. If the system settings interface + did not present itself, this test did *not* fail. + + +Hud Key-Navigation +----------- +This test ensures the hud key navigation is intact + +#. Ensure the messaging indicator is present +#. Tap Alt +#. Type "Message" to search for items from the messaging indicator +#. Press down twice +#. Press return + +Outcome + The item selected will activate and the hud with disappear. + If the hud does not disappear, this test failed. + If the buttons under the search box do not highlight, this test failed. + + +Hud Dismiss +---------- +This test ensures that the hud is dismissable + +#. Tap Alt +#. Type "test" +#. Press escape +#. Click anywhere on the screen that is not the hud interface + +Outcome + After pressing escape in step three, the text "test" should be removed from the hud search + After step four, the hud should dismiss itself and not be present. + diff --git a/manual-tests/Showdesktop.txt b/manual-tests/Showdesktop.txt deleted file mode 100644 index e06ca4704..000000000 --- a/manual-tests/Showdesktop.txt +++ /dev/null @@ -1,15 +0,0 @@ -Test Showdesktop Mode -------------------- -This test shows that the "show desktop" mode works correctly - -#. Open two applications -#. Use either alt-tab or ctrl-alt-d to activate "show desktop" mode -#. Use either alt-tab or ctrl-alt-d to deactivate "show desktop" mode -#. Use either alt-tab or ctrl-alt-d to activate "show desktop" mode -#. Select an active application from the launcher -#. Use either alt-tab or ctrl-alt-d to deactivate "show desktop" mode - -Outcome - Both windows will fade out, both windows will fade in, both windows - will fade out, the clicked application will fade in only, all other - windows will fade in. diff --git a/plugins/unityshell/resources/close_dash.png b/plugins/unityshell/resources/close_dash.png Binary files differindex 876d39e41..3925bda11 100644 --- a/plugins/unityshell/resources/close_dash.png +++ b/plugins/unityshell/resources/close_dash.png diff --git a/plugins/unityshell/resources/close_dash_prelight.png b/plugins/unityshell/resources/close_dash_prelight.png Binary files differindex 876d39e41..2d6233509 100644 --- a/plugins/unityshell/resources/close_dash_prelight.png +++ b/plugins/unityshell/resources/close_dash_prelight.png diff --git a/plugins/unityshell/resources/close_dash_pressed.png b/plugins/unityshell/resources/close_dash_pressed.png Binary files differindex 90c1f7e89..bdcc40787 100644 --- a/plugins/unityshell/resources/close_dash_pressed.png +++ b/plugins/unityshell/resources/close_dash_pressed.png diff --git a/plugins/unityshell/resources/maximize_dash.png b/plugins/unityshell/resources/maximize_dash.png Binary files differindex a9102870c..6c48f11da 100644 --- a/plugins/unityshell/resources/maximize_dash.png +++ b/plugins/unityshell/resources/maximize_dash.png diff --git a/plugins/unityshell/resources/maximize_dash_prelight.png b/plugins/unityshell/resources/maximize_dash_prelight.png Binary files differindex b9ce8f341..05d4d4653 100644 --- a/plugins/unityshell/resources/maximize_dash_prelight.png +++ b/plugins/unityshell/resources/maximize_dash_prelight.png diff --git a/plugins/unityshell/resources/maximize_dash_pressed.png b/plugins/unityshell/resources/maximize_dash_pressed.png Binary files differindex 739d07a02..a1b26e20d 100644 --- a/plugins/unityshell/resources/maximize_dash_pressed.png +++ b/plugins/unityshell/resources/maximize_dash_pressed.png diff --git a/plugins/unityshell/resources/minimize_dash.png b/plugins/unityshell/resources/minimize_dash.png Binary files differindex c934104d0..05352beca 100644 --- a/plugins/unityshell/resources/minimize_dash.png +++ b/plugins/unityshell/resources/minimize_dash.png diff --git a/plugins/unityshell/resources/minimize_dash_prelight.png b/plugins/unityshell/resources/minimize_dash_prelight.png Binary files differindex c934104d0..05352beca 100644 --- a/plugins/unityshell/resources/minimize_dash_prelight.png +++ b/plugins/unityshell/resources/minimize_dash_prelight.png diff --git a/plugins/unityshell/resources/minimize_dash_pressed.png b/plugins/unityshell/resources/minimize_dash_pressed.png Binary files differindex c934104d0..05352beca 100644 --- a/plugins/unityshell/resources/minimize_dash_pressed.png +++ b/plugins/unityshell/resources/minimize_dash_pressed.png diff --git a/plugins/unityshell/resources/unmaximize_dash.png b/plugins/unityshell/resources/unmaximize_dash.png Binary files differindex 133e7058b..9cd411bea 100644 --- a/plugins/unityshell/resources/unmaximize_dash.png +++ b/plugins/unityshell/resources/unmaximize_dash.png diff --git a/plugins/unityshell/resources/unmaximize_dash_prelight.png b/plugins/unityshell/resources/unmaximize_dash_prelight.png Binary files differindex 133e7058b..2b836fd14 100644 --- a/plugins/unityshell/resources/unmaximize_dash_prelight.png +++ b/plugins/unityshell/resources/unmaximize_dash_prelight.png diff --git a/plugins/unityshell/resources/unmaximize_dash_pressed.png b/plugins/unityshell/resources/unmaximize_dash_pressed.png Binary files differindex f2bdc3a9f..ad305cb04 100644 --- a/plugins/unityshell/resources/unmaximize_dash_pressed.png +++ b/plugins/unityshell/resources/unmaximize_dash_pressed.png diff --git a/plugins/unityshell/src/DashController.cpp b/plugins/unityshell/src/DashController.cpp index bd994f99d..da1cd0159 100644 --- a/plugins/unityshell/src/DashController.cpp +++ b/plugins/unityshell/src/DashController.cpp @@ -118,6 +118,18 @@ void Controller::RegisterUBusInterests() sigc::mem_fun(this, &Controller::OnActivateRequest)); ubus_manager_.RegisterInterest(UBUS_DASH_ABOUT_TO_SHOW, [&] (GVariant*) { EnsureDash(); }); + ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, [&] (GVariant *data) { + unity::glib::String overlay_identity; + gboolean can_maximise = FALSE; + gint32 overlay_monitor = 0; + g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor); + + // hide if something else is coming up + if (g_strcmp0(overlay_identity, "dash")) + { + HideDash(true); + } + }); } void Controller::EnsureDash() diff --git a/plugins/unityshell/src/DashStyle.cpp b/plugins/unityshell/src/DashStyle.cpp index 94f819f62..a3f39c585 100644 --- a/plugins/unityshell/src/DashStyle.cpp +++ b/plugins/unityshell/src/DashStyle.cpp @@ -119,6 +119,7 @@ public: void Text(cairo_t* cr, nux::Color const& color, std::string const& label, + int font_size = -1, double horizMargin = 10.0, Alignment alignment = Alignment::CENTER); @@ -1325,6 +1326,7 @@ void Style::Impl::GetTextExtents(int& width, void Style::Impl::Text(cairo_t* cr, nux::Color const& color, std::string const& label, + int text_size, double horizMargin, Alignment alignment) { @@ -1358,6 +1360,11 @@ void Style::Impl::Text(cairo_t* cr, else desc = pango_font_description_from_string(fontName); + if (text_size > 0) + { + pango_font_description_set_absolute_size(desc, text_size * PANGO_SCALE); + } + PangoWeight weight; switch (regular_text_weight_) { @@ -1428,6 +1435,7 @@ void Style::Impl::Text(cairo_t* cr, pango_layout_get_extents(layout, &ink, &log); x = horizMargin; // let pango alignment handle the x position y = ((double) h - pango_units_to_double(log.height)) / 2.0; + cairo_move_to(cr, x, y); pango_cairo_show_layout(cr, layout); @@ -1530,7 +1538,9 @@ void Style::Impl::DrawOverlay(cairo_t* cr, cairo_set_operator(cr, old); } -bool Style::Button(cairo_t* cr, nux::ButtonVisualState state, std::string const& label, Alignment alignment) +bool Style::Button(cairo_t* cr, nux::ButtonVisualState state, + std::string const& label, int font_size, + Alignment alignment, bool zeromargin) { // sanity checks if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) @@ -1539,8 +1549,10 @@ bool Style::Button(cairo_t* cr, nux::ButtonVisualState state, std::string const& if (cairo_surface_get_type(cairo_get_target(cr)) != CAIRO_SURFACE_TYPE_IMAGE) return false; - unsigned int garnish = GetButtonGarnishSize(); - + unsigned int garnish = 0; + if (zeromargin == false) + garnish = GetButtonGarnishSize(); + //ButtonOutlinePath(cr, true); double w = cairo_image_surface_get_width(cairo_get_target(cr)); double h = cairo_image_surface_get_height(cairo_get_target(cr)); @@ -1571,7 +1583,6 @@ bool Style::Button(cairo_t* cr, nux::ButtonVisualState state, std::string const& cairo_fill_preserve(cr); } cairo_set_source_rgba(cr, pimpl->button_label_border_color_[state]); - //cairo_set_line_width(cr, pimpl->button_label_border_size_[state]); cairo_stroke(cr); pimpl->DrawOverlay(cr, @@ -1582,12 +1593,133 @@ bool Style::Button(cairo_t* cr, nux::ButtonVisualState state, std::string const& pimpl->Text(cr, pimpl->button_label_text_color_[state], label, + font_size, 10.0, alignment); return true; } +bool Style::SquareButton(cairo_t* cr, nux::ButtonVisualState state, + std::string const& label, bool curve_bottom, + int font_size, Alignment alignment, + bool zeromargin) +{ + // sanity checks + if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) + return false; + + if (cairo_surface_get_type(cairo_get_target(cr)) != CAIRO_SURFACE_TYPE_IMAGE) + return false; + + unsigned int garnish = 0; + if (zeromargin == false) + garnish = GetButtonGarnishSize(); + + double w = cairo_image_surface_get_width(cairo_get_target(cr)); + double h = cairo_image_surface_get_height(cairo_get_target(cr)); + + double x = garnish; + double y = garnish; + + double width = w - (2.0 * garnish) - 1.0; + double height = h - (2.0 * garnish) - 1.0; + + bool odd = true; + double radius = 7.0; + + // draw the grid background + { + cairo_set_line_width(cr, 1); + cairo_move_to(cr, _align(x + width, odd), _align(y, odd)); + if (curve_bottom) + { + LOG_DEBUG(logger) << "curve: " << _align(x + width, odd) << " - " << _align(y + height - radius, odd); + // line to bottom-right corner + cairo_line_to(cr, _align(x + width, odd), _align(y + height - radius, odd)); + + // line to bottom-right, left of the corner + cairo_arc(cr, + _align(x + width - radius, odd), + _align(y + height - radius, odd), + radius, + 0.0f * G_PI / 180.0f, + 90.0f * G_PI / 180.0f); + + // line to bottom-left, right of the corner + cairo_line_to(cr, _align(x + radius, odd), _align(y + height, odd)); + + // line to bottom-left, above the corner + cairo_arc(cr, + _align(x + radius, odd), + _align(y + height - radius, odd), + radius, + 90.0f * G_PI / 180.0f, + 180.0f * G_PI / 180.0f); + + // line to top + cairo_line_to(cr, _align(x, odd), _align(y, odd)); + } + else + { + cairo_line_to(cr, _align(x + width, odd), _align(y + height, odd)); + cairo_line_to(cr, _align(x, odd), _align(x + height, odd)); + cairo_line_to(cr, _align(x, odd), _align(y, odd)); + } + + cairo_set_source_rgba(cr, pimpl->button_label_border_color_[nux::ButtonVisualState::VISUAL_STATE_NORMAL]); + cairo_stroke(cr); + } + + cairo_set_line_width(cr, pimpl->button_label_border_size_[state]); + odd = cairo_get_line_width(cr) == 2.0 ? false : true; + + + if (pimpl->button_label_border_size_[state] == 2.0) + { + x += 1; + y += 1; + width -= 1.0; + height -= 1.0; + } + + if (state == nux::ButtonVisualState::VISUAL_STATE_PRESSED) + { + RoundedRect(cr, + 1.0, + _align(x, odd), _align(y, odd), + 5.0, + _align(width, odd), _align(height, odd)); + + if (pimpl->button_label_fill_color_[state].alpha != 0.0) + { + cairo_set_source_rgba(cr, pimpl->button_label_fill_color_[state]); + cairo_fill_preserve(cr); + } + cairo_set_source_rgba(cr, pimpl->button_label_border_color_[state]); + cairo_stroke(cr); + } + + pimpl->DrawOverlay(cr, + pimpl->button_label_overlay_opacity_[state], + pimpl->button_label_overlay_mode_[state], + pimpl->button_label_blur_size_[state] * 0.75); + + // FIXME - magic value of 42 here for the offset in the HUD, + // replace with a nicer style system that lets hud override + // default values when it needs to + pimpl->Text(cr, + pimpl->button_label_text_color_[state], + label, + font_size, + 42.0 + 10.0, + alignment); + + cairo_surface_write_to_png(cairo_get_target(cr), "/tmp/wut.png"); + + return true; +} + bool Style::StarEmpty(cairo_t* cr, nux::ButtonVisualState state) { // sanity checks diff --git a/plugins/unityshell/src/DashStyle.h b/plugins/unityshell/src/DashStyle.h index 0c06244f6..ba10f7c9d 100644 --- a/plugins/unityshell/src/DashStyle.h +++ b/plugins/unityshell/src/DashStyle.h @@ -87,8 +87,15 @@ public: static Style& Instance(); virtual bool Button(cairo_t* cr, nux::ButtonVisualState state, - std::string const& label, - Alignment alignment = Alignment::CENTER); + std::string const& label, int font_size=-1, + Alignment alignment = Alignment::CENTER, + bool zeromargin=false); + + virtual bool SquareButton(cairo_t* cr, nux::ButtonVisualState state, + std::string const& label, bool curve_bottom, + int font_size=-1, + Alignment alignment = Alignment::CENTER, + bool zeromargin=false); virtual bool StarEmpty(cairo_t* cr, nux::ButtonVisualState state); diff --git a/plugins/unityshell/src/DashView.cpp b/plugins/unityshell/src/DashView.cpp index bde256eee..c341bd137 100644 --- a/plugins/unityshell/src/DashView.cpp +++ b/plugins/unityshell/src/DashView.cpp @@ -154,6 +154,7 @@ void DashView::SetupViews() lenses_layout_->AddView(home_view_); lens_bar_ = new LensBar(); + AddChild(lens_bar_); lens_bar_->lens_activated.connect(sigc::mem_fun(this, &DashView::OnLensBarActivated)); content_layout_->AddView(lens_bar_, 0, nux::MINOR_POSITION_CENTER); } diff --git a/plugins/unityshell/src/DashView.h b/plugins/unityshell/src/DashView.h index 938950665..80b64ada5 100644 --- a/plugins/unityshell/src/DashView.h +++ b/plugins/unityshell/src/DashView.h @@ -30,7 +30,7 @@ #include <UnityCore/HomeLens.h> #include "BackgroundEffectHelper.h" -#include "DashSearchBar.h" +#include "SearchBar.h" #include "Introspectable.h" #include "LensBar.h" #include "LensView.h" diff --git a/plugins/unityshell/src/DeviceLauncherIcon.cpp b/plugins/unityshell/src/DeviceLauncherIcon.cpp index eca15f17a..ec0e5ce4e 100644 --- a/plugins/unityshell/src/DeviceLauncherIcon.cpp +++ b/plugins/unityshell/src/DeviceLauncherIcon.cpp @@ -130,6 +130,7 @@ std::list<DbusmenuMenuitem*> DeviceLauncherIcon::GetMenus() { menu_item = dbusmenu_menuitem_new(); + // TRANSLATORS: This refers to the action of formatting a device dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Format...")); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); diff --git a/plugins/unityshell/src/FilterAllButton.cpp b/plugins/unityshell/src/FilterAllButton.cpp index e3473d2ad..dc42e94f2 100644 --- a/plugins/unityshell/src/FilterAllButton.cpp +++ b/plugins/unityshell/src/FilterAllButton.cpp @@ -59,6 +59,7 @@ void FilterAllButton::OnStateChanged(nux::View* view) { if (filter_ and Active()) filter_->Clear(); + QueueDraw(); } void FilterAllButton::OnFilteringChanged(bool filtering) diff --git a/plugins/unityshell/src/HudButton.cpp b/plugins/unityshell/src/HudButton.cpp new file mode 100644 index 000000000..269ee401f --- /dev/null +++ b/plugins/unityshell/src/HudButton.cpp @@ -0,0 +1,208 @@ +/* + * Copyright 2011 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: Gordon Allott <gord.allott@canonical.com> + * + */ +#include "config.h" + +#include <pango/pango.h> +#include <pango/pangocairo.h> +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +#include <Nux/Nux.h> +#include <NuxCore/Logger.h> +#include <NuxImage/CairoGraphics.h> +#include <NuxGraphics/NuxGraphics.h> +#include <UnityCore/GLibWrapper.h> +#include "DashStyle.h" + +#include "HudButton.h" + +namespace +{ +nux::logging::Logger logger("unity.hud.HudButton"); +} + +namespace unity +{ +namespace hud +{ + + +HudButton::HudButton (nux::TextureArea *image, NUX_FILE_LINE_DECL) + : nux::Button (image, NUX_FILE_LINE_PARAM) + , is_rounded(false) + , is_focused_(false) +{ + InitTheme(); + key_nav_focus_change.connect([this](nux::Area *area, bool recieving, nux::KeyNavDirection direction){ QueueDraw(); }); +} + +HudButton::HudButton (const std::string label_, NUX_FILE_LINE_DECL) + : nux::Button (NUX_FILE_LINE_PARAM) + , is_rounded(false) + , is_focused_(false) +{ + InitTheme(); +} + +HudButton::HudButton (const std::string label_, nux::TextureArea *image, NUX_FILE_LINE_DECL) + : nux::Button (image, NUX_FILE_LINE_PARAM) + , is_rounded(false) + , is_focused_(false) +{ + InitTheme(); +} + +HudButton::HudButton (NUX_FILE_LINE_DECL) + : nux::Button (NUX_FILE_LINE_PARAM) + , is_rounded(false) + , is_focused_(false) +{ + InitTheme(); +} + +HudButton::~HudButton() { +} + +void HudButton::InitTheme() +{ + is_rounded.changed.connect([&] (bool rounded) + { + nux::Geometry geo = GetGeometry(); + prelight_->Invalidate(geo); + active_->Invalidate(geo); + normal_->Invalidate(geo); + }); + + SetMinimumHeight(42); + if (!active_) + { + nux::Geometry const& geo = GetGeometry(); + + prelight_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &HudButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_PRELIGHT))); + active_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &HudButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_PRESSED))); + normal_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &HudButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_NORMAL))); + } +} + +void HudButton::RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state) +{ + dash::Style::Instance().SquareButton(cr, faked_state, label_, + is_rounded, 17, + dash::Alignment::LEFT, true); +} + +bool HudButton::AcceptKeyNavFocus() +{ + return true; +} + + +long HudButton::ComputeContentSize () +{ + long ret = nux::Button::ComputeContentSize(); + nux::Geometry const& geo = GetGeometry(); + + if (cached_geometry_ != geo) + { + prelight_->Invalidate(geo); + active_->Invalidate(geo); + normal_->Invalidate(geo); + + cached_geometry_ = geo; + } + + return ret; +} + +void HudButton::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) +{ + nux::Geometry const& geo = GetGeometry(); + gPainter.PaintBackground(GfxContext, geo); + // set up our texture mode + nux::TexCoordXForm texxform; + texxform.SetWrap(nux::TEXWRAP_CLAMP, nux::TEXWRAP_CLAMP); + texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); + + // clear what is behind us + unsigned int alpha = 0, src = 0, dest = 0; + GfxContext.GetRenderStates().GetBlend(alpha, src, dest); + GfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER); + GfxContext.GetRenderStates().SetBlend(true); + + nux::Color col = nux::color::Black; + col.alpha = 0; + GfxContext.QRP_Color(geo.x, + geo.y, + geo.width, + geo.height, + col); + + nux::BaseTexture* texture = normal_->GetTexture(); + if (HasKeyFocus()) + texture = active_->GetTexture(); + else if (HasKeyFocus()) + texture = prelight_->GetTexture(); + else if (GetVisualState() == nux::ButtonVisualState::VISUAL_STATE_PRESSED) + texture = active_->GetTexture(); + + GfxContext.QRP_1Tex(geo.x, + geo.y, + texture->GetWidth() + 1, // FIXME !! - jay, nux has gone crazy, unless i specify +1 here, it won't render the entire texture + texture->GetHeight(), + texture->GetDeviceTexture(), + texxform, + nux::Color(1.0f, 1.0f, 1.0f, 1.0f)); + + GfxContext.GetRenderStates().SetBlend(alpha, src, dest); +} + +void HudButton::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) { +} + +void HudButton::PostDraw(nux::GraphicsEngine& GfxContext, bool force_draw) { + nux::Button::PostDraw(GfxContext, force_draw); +} + +void HudButton::SetQuery(Query::Ptr query) +{ + query_ = query; + label_ = query->formatted_text; + label = query->formatted_text; +} + +Query::Ptr HudButton::GetQuery() +{ + return query_; +} + +// Introspectable +std::string HudButton::GetName() const +{ + return "HudButton"; +} + +void HudButton::AddProperties(GVariantBuilder* builder) +{ + g_variant_builder_add(builder, "{sv}", "label", g_variant_new_string(label_.c_str())); +} + +} +} diff --git a/plugins/unityshell/src/HudButton.h b/plugins/unityshell/src/HudButton.h new file mode 100644 index 000000000..ef55760d3 --- /dev/null +++ b/plugins/unityshell/src/HudButton.h @@ -0,0 +1,81 @@ +/* + * Copyright 2011 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: Gordon Allott <gord.allott@canonical.com> + * + */ + + + +#ifndef FILTERBASICBUTTON_H +#define FILTERBASICBUTTON_H + +#include <Nux/Nux.h> +#include <Nux/CairoWrapper.h> +#include <Nux/Button.h> +#include <Nux/TextureArea.h> +#include <UnityCore/Hud.h> +#include "Introspectable.h" + +namespace unity { +namespace hud { +class HudButton : public nux::Button, public unity::debug::Introspectable +{ + typedef nux::ObjectPtr<nux::BaseTexture> BaseTexturePtr; +public: + typedef nux::ObjectPtr<HudButton> Ptr; + HudButton (nux::TextureArea *image, NUX_FILE_LINE_PROTO); + HudButton (const std::string label, NUX_FILE_LINE_PROTO); + HudButton (const std::string label, nux::TextureArea *image, NUX_FILE_LINE_PROTO); + HudButton (NUX_FILE_LINE_PROTO); + virtual ~HudButton(); + + void SetQuery(Query::Ptr query); + std::shared_ptr<Query> GetQuery(); + + nux::Property<std::string> label; + nux::Property<std::string> hint; + nux::Property<bool> is_rounded; +protected: + + virtual bool AcceptKeyNavFocus(); + virtual long ComputeContentSize(); + virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); + virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); + virtual void PostDraw(nux::GraphicsEngine& GfxContext, bool force_draw); + + std::string GetName() const; + void AddProperties(GVariantBuilder* builder); + + void InitTheme (); + void RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state); + typedef std::unique_ptr<nux::CairoWrapper> NuxCairoPtr; + + NuxCairoPtr prelight_; + NuxCairoPtr active_; + NuxCairoPtr normal_; + +private: + std::string label_; + + Query::Ptr query_; + nux::Geometry cached_geometry_; + bool is_focused_; +}; +} +} +#endif // FILTERBASICBUTTON_H diff --git a/plugins/unityshell/src/HudController.cpp b/plugins/unityshell/src/HudController.cpp new file mode 100644 index 000000000..bf02375f2 --- /dev/null +++ b/plugins/unityshell/src/HudController.cpp @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2010 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: Gord Allott <gord.allott@canonical.com> + */ + +#include "HudController.h" + +#include <NuxCore/Logger.h> +#include <Nux/HLayout.h> +#include "PluginAdapter.h" +#include "UBusMessages.h" +#include "UScreen.h" +namespace unity +{ +namespace hud +{ + +namespace +{ +nux::logging::Logger logger("unity.hud.controller"); +} + +Controller::Controller() + : launcher_width(66) + , panel_height(24) + , hud_service_("com.canonical.hud", "/com/canonical/hud") + , window_(0) + , visible_(false) + , need_show_(false) + , timeline_id_(0) + , last_opacity_(0.0f) + , start_time_(0) +{ + LOG_DEBUG(logger) << "hud startup"; + SetupRelayoutCallbacks(); + + ubus.RegisterInterest(UBUS_HUD_CLOSE_REQUEST, sigc::mem_fun(this, &Controller::OnExternalHideHud)); + + //!!FIXME!! - just hijacks the dash close request so we get some more requests than normal, + ubus.RegisterInterest(UBUS_PLACE_VIEW_CLOSE_REQUEST, sigc::mem_fun(this, &Controller::OnExternalHideHud)); + + ubus.RegisterInterest(UBUS_OVERLAY_SHOWN, [&] (GVariant *data) { + unity::glib::String overlay_identity; + gboolean can_maximise = FALSE; + gint32 overlay_monitor = 0; + g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor); + + if (g_strcmp0(overlay_identity, "hud")) + { + HideHud(true); + } + }); + + PluginAdapter::Default()->compiz_screen_ungrabbed.connect(sigc::mem_fun(this, &Controller::OnScreenUngrabbed)); + + hud_service_.queries_updated.connect(sigc::mem_fun(this, &Controller::OnQueriesFinished)); + EnsureHud(); +} + +Controller::~Controller() +{ + if (window_) + window_->UnReference(); + window_ = 0; + + g_source_remove(timeline_id_); + g_source_remove(ensure_id_); +} + +void Controller::SetupWindow() +{ + window_ = new nux::BaseWindow("Hud"); + window_->SinkReference(); + window_->SetBackgroundColor(nux::Color(0.0f, 0.0f, 0.0f, 0.0f)); + window_->SetConfigureNotifyCallback(&Controller::OnWindowConfigure, this); + window_->ShowWindow(false); + window_->SetOpacity(0.0f); + window_->mouse_down_outside_pointer_grab_area.connect(sigc::mem_fun(this, &Controller::OnMouseDownOutsideWindow)); +} + +void Controller::SetupHudView() +{ + LOG_DEBUG(logger) << "SetupHudView called"; + view_ = new View(); + + layout_ = new nux::HLayout(NUX_TRACKER_LOCATION); + layout_->AddView(view_, 1); + window_->SetLayout(layout_); + + view_->mouse_down_outside_pointer_grab_area.connect(sigc::mem_fun(this, &Controller::OnMouseDownOutsideWindow)); + + LOG_DEBUG(logger) << "connecting to signals"; + view_->search_changed.connect(sigc::mem_fun(this, &Controller::OnSearchChanged)); + view_->search_activated.connect(sigc::mem_fun(this, &Controller::OnSearchActivated)); + view_->query_activated.connect(sigc::mem_fun(this, &Controller::OnQueryActivated)); + view_->query_selected.connect(sigc::mem_fun(this, &Controller::OnQuerySelected)); +} + +void Controller::SetupRelayoutCallbacks() +{ + GdkScreen* screen = gdk_screen_get_default(); + + sig_manager_.Add(new glib::Signal<void, GdkScreen*>(screen, + "monitors-changed", sigc::mem_fun(this, &Controller::Relayout))); + sig_manager_.Add(new glib::Signal<void, GdkScreen*>(screen, + "size-changed", sigc::mem_fun(this, &Controller::Relayout))); +} + +void Controller::EnsureHud() +{ + if (window_) + return; + + LOG_DEBUG(logger) << "Initializing Hud"; + + SetupWindow(); + SetupHudView(); + Relayout(); + ensure_id_ = 0; +} + +nux::BaseWindow* Controller::window() const +{ + return window_; +} + +// We update the @geo that's sent in with our desired width and height +void Controller::OnWindowConfigure(int window_width, int window_height, + nux::Geometry& geo, void* data) +{ + Controller* self = static_cast<Controller*>(data); + geo = self->GetIdealWindowGeometry(); +} + +nux::Geometry Controller::GetIdealWindowGeometry() +{ + UScreen *uscreen = UScreen::GetDefault(); + int primary_monitor = uscreen->GetPrimaryMonitor(); + auto monitor_geo = uscreen->GetMonitorGeometry(primary_monitor); + + // We want to cover as much of the screen as possible to grab any mouse events outside + // of our window + return nux::Geometry (monitor_geo.x, + monitor_geo.y + panel_height, + monitor_geo.width, + monitor_geo.height - panel_height); +} + +void Controller::Relayout(GdkScreen*screen) +{ + EnsureHud(); + nux::Geometry content_geo = view_->GetGeometry(); + nux::Geometry geo = GetIdealWindowGeometry(); + + window_->SetGeometry(geo); + layout_->SetMinMaxSize(content_geo.width, content_geo.height); + view_->SetWindowGeometry(window_->GetAbsoluteGeometry(), window_->GetGeometry()); + view_->Relayout(); +} + +void Controller::OnMouseDownOutsideWindow(int x, int y, + unsigned long bflags, unsigned long kflags) +{ + LOG_DEBUG(logger) << "OnMouseDownOutsideWindow called"; + HideHud(); +} + +void Controller::OnScreenUngrabbed() +{ + LOG_DEBUG(logger) << "OnScreenUngrabbed called"; + if (need_show_) + { + EnsureHud(); + ShowHud(); + } +} + +void Controller::OnExternalShowHud(GVariant* variant) +{ + EnsureHud(); + visible_ ? HideHud() : ShowHud(); +} + +void Controller::OnExternalHideHud(GVariant* variant) +{ + LOG_DEBUG(logger) << "External Hiding the hud"; + EnsureHud(); + HideHud(); +} + +void Controller::ShowHideHud() +{ + EnsureHud(); + visible_ ? HideHud(true) : ShowHud(); +} + +bool Controller::IsVisible() +{ + return visible_; +} + +void Controller::ShowHud() +{ + LOG_DEBUG(logger) << "Showing the hud"; + EnsureHud(); + view_->AboutToShow(); + + window_->ShowWindow(true); + window_->PushToFront(); + window_->EnableInputWindow(true, "Hud", true, false); + window_->SetInputFocus(); + nux::GetWindowCompositor().SetKeyFocusArea(view_->default_focus()); + window_->CaptureMouseDownAnyWhereElse(true); + view_->CaptureMouseDownAnyWhereElse(true); + window_->QueueDraw(); + + view_->ResetToDefault(); + + view_->SetIcon(""); + hud_service_.RequestQuery(""); + need_show_ = false; + visible_ = true; + + StartShowHideTimeline(); + view_->SetWindowGeometry(window_->GetAbsoluteGeometry(), window_->GetGeometry()); + + // hide the launcher + GVariant* message_data = g_variant_new("(b)", TRUE); + ubus.SendMessage(UBUS_LAUNCHER_LOCK_HIDE, message_data); + + GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "hud", FALSE, 0); + ubus.SendMessage(UBUS_OVERLAY_SHOWN, info); +} +void Controller::HideHud(bool restore) +{ + LOG_DEBUG (logger) << "hiding the hud"; + if (visible_ == false) + return; + + EnsureHud(); + view_->AboutToHide(); + window_->CaptureMouseDownAnyWhereElse(false); + window_->EnableInputWindow(false, "Hud", true, false); + visible_ = false; + + StartShowHideTimeline(); + + restore = true; + if (restore) + PluginAdapter::Default ()->restoreInputFocus (); + + hud_service_.CloseQuery(); + + //unhide the launcher + GVariant* message_data = g_variant_new("(b)", FALSE); + ubus.SendMessage(UBUS_LAUNCHER_LOCK_HIDE, message_data); + + GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "hud", FALSE, 0); + ubus.SendMessage(UBUS_OVERLAY_HIDDEN, info); +} + +void Controller::StartShowHideTimeline() +{ + EnsureHud(); + + if (timeline_id_) + g_source_remove(timeline_id_); + + timeline_id_ = g_timeout_add(15, (GSourceFunc)Controller::OnViewShowHideFrame, this); + last_opacity_ = window_->GetOpacity(); + start_time_ = g_get_monotonic_time(); + +} + +gboolean Controller::OnViewShowHideFrame(Controller* self) +{ +#define _LENGTH_ 90000 + float diff = g_get_monotonic_time() - self->start_time_; + float progress = diff / (float)_LENGTH_; + float last_opacity = self->last_opacity_; + + if (self->visible_) + { + self->window_->SetOpacity(last_opacity + ((1.0f - last_opacity) * progress)); + } + else + { + self->window_->SetOpacity(last_opacity - (last_opacity * progress)); + } + + if (diff > _LENGTH_) + { + self->timeline_id_ = 0; + + // Make sure the state is right + self->window_->SetOpacity(self->visible_ ? 1.0f : 0.0f); + if (!self->visible_) + { + self->window_->ShowWindow(false); + } + + return FALSE; + } + + return TRUE; +} + +void Controller::OnActivateRequest(GVariant* variant) +{ + EnsureHud(); + ShowHud(); +} + +void Controller::OnSearchChanged(std::string search_string) +{ + LOG_DEBUG(logger) << "Search Changed"; + hud_service_.RequestQuery(search_string); +} + +void Controller::OnSearchActivated(std::string search_string) +{ + unsigned int timestamp = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent().x11_timestamp; + hud_service_.ExecuteQueryBySearch(search_string, timestamp); + HideHud(); +} + +void Controller::OnQueryActivated(Query::Ptr query) +{ + LOG_DEBUG(logger) << "Activating query, " << query->formatted_text; + unsigned int timestamp = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent().x11_timestamp; + hud_service_.ExecuteQuery(query, timestamp); + HideHud(); +} + +void Controller::OnQuerySelected(Query::Ptr query) +{ + LOG_DEBUG(logger) << "Selected query, " << query->formatted_text; + view_->SetIcon(query->icon_name); +} + + +void Controller::OnQueriesFinished(Hud::Queries queries) +{ + view_->SetQueries(queries); + std::string icon_name = ""; + for (auto query = queries.begin(); query != queries.end(); query++) + { + if (!(*query)->icon_name.empty()) + { + icon_name = (*query)->icon_name; + break; + } + } + + LOG_DEBUG(logger) << "setting icon to - " << icon_name; + view_->SetIcon(icon_name); +} + +// Introspectable +std::string Controller::GetName() const +{ + return "HudController"; +} + +void Controller::AddProperties(GVariantBuilder* builder) +{ + g_variant_builder_add(builder, "{sv}", "visible", g_variant_new_boolean (visible_)); +} + + +} +} diff --git a/plugins/unityshell/src/HudController.h b/plugins/unityshell/src/HudController.h new file mode 100644 index 000000000..211842404 --- /dev/null +++ b/plugins/unityshell/src/HudController.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2010 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: Neil Jagdish Patel <neil.patel@canonical.com> + */ + +#ifndef UNITY_HUD_CONTROLLER_H_ +#define UNITY_HUD_CONTROLLER_H_ + +#include <memory> + +#include <gdk/gdk.h> +#include <UnityCore/GLibSignal.h> +#include <UnityCore/Hud.h> + +#include <NuxCore/Property.h> +#include <NuxGraphics/GraphicsEngine.h> +#include <Nux/Nux.h> +#include <Nux/BaseWindow.h> + +#include "HudView.h" +#include "UBusWrapper.h" + +namespace unity +{ +namespace hud +{ + +class Controller : public unity::debug::Introspectable +{ +public: + typedef std::shared_ptr<Controller> Ptr; + + Controller(); + ~Controller(); + + nux::BaseWindow* window() const; + + nux::Property<int> launcher_width; + nux::Property<int> panel_height; + + void ShowHideHud(); + void ShowHud(); + void HideHud(bool restore_focus = true); + bool IsVisible(); +protected: + std::string GetName() const; + void AddProperties(GVariantBuilder* builder); + +private: + void EnsureHud(); + void SetupWindow(); + void SetupHudView(); + void SetupRelayoutCallbacks(); + void RegisterUBusInterests(); + + nux::Geometry GetIdealWindowGeometry(); + void Relayout(GdkScreen*screen=NULL); + + void OnMouseDownOutsideWindow(int x, int y, unsigned long bflags, unsigned long kflags); + void OnScreenUngrabbed(); + void OnExternalShowHud(GVariant* variant); + void OnExternalHideHud(GVariant* variant); + void OnActivateRequest(GVariant* variant); + + void OnSearchChanged(std::string search_string); + void OnSearchActivated(std::string search_string); + void OnQueryActivated(Query::Ptr query); + void OnQuerySelected(Query::Ptr query); + + +private: + void StartShowHideTimeline(); + static gboolean OnViewShowHideFrame(Controller* self); + + static void OnWindowConfigure(int width, int height, nux::Geometry& geo, void* data); + + void OnQueriesFinished(Hud::Queries queries); + +private: + UBusManager ubus; + Hud hud_service_; + glib::SignalManager sig_manager_; + nux::BaseWindow* window_; + bool visible_; + bool need_show_; + + guint timeline_id_; + float last_opacity_; + gint64 start_time_; + + View* view_; + guint ensure_id_; + + nux::Layout* layout_; +}; + + +} +} +#endif diff --git a/plugins/unityshell/src/HudIcon.cpp b/plugins/unityshell/src/HudIcon.cpp new file mode 100644 index 000000000..d0128ff42 --- /dev/null +++ b/plugins/unityshell/src/HudIcon.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2010 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: Gord Allott <gord.allott@canonical.com> + */ + + +#include "HudIcon.h" +#include "NuxCore/Logger.h" +namespace +{ + nux::logging::Logger logger("unity.hud.icon"); +} + +namespace unity +{ +namespace hud +{ + +Icon::Icon(nux::BaseTexture* texture, guint width, guint height) + : unity::IconTexture(texture, width, height) +{ + Init(); + icon_renderer_.SetTargetSize(54, 46, 0); +} + +Icon::Icon(const char* icon_name, unsigned int size, bool defer_icon_loading) + : unity::IconTexture(icon_name, size, defer_icon_loading) +{ + Init(); +} + +Icon::~Icon() +{ +} + +void Icon::Init() +{ + SetMinimumWidth(66); + SetMinimumHeight(66); + background_ = nux::CreateTexture2DFromFile(PKGDATADIR"/launcher_icon_back_54.png", -1, true); + gloss_ = nux::CreateTexture2DFromFile(PKGDATADIR"/launcher_icon_shine_54.png", -1, true); + edge_ = nux::CreateTexture2DFromFile(PKGDATADIR"/launcher_icon_edge_54.png", -1, true); + + texture_updated.connect([&] (nux::BaseTexture* texture) + { + icon_texture_source_ = new HudIconTextureSource(nux::ObjectPtr<nux::BaseTexture>(texture)); + icon_texture_source_->ColorForIcon(_pixbuf_cached); + QueueDraw(); + LOG_DEBUG(logger) << "got our texture"; + }); +} + +void Icon::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) +{ + if (texture() == nullptr) + return; + + unity::ui::RenderArg arg; + arg.icon = icon_texture_source_.GetPointer(); + arg.colorify = nux::color::White; + arg.running_arrow = true; + arg.running_on_viewport = true; + arg.render_center = nux::Point3(32, 32, 0); + arg.logical_center = nux::Point3(52, 50, 0); + arg.window_indicators = true; + arg.backlight_intensity = 1.0f; + arg.alpha = 1.0f; + + std::list<unity::ui::RenderArg> args; + args.push_front(arg); + + + auto toplevel = GetToplevel(); + icon_renderer_.SetTargetSize(54, 46, 0); + icon_renderer_.PreprocessIcons(args, toplevel->GetGeometry()); + icon_renderer_.RenderIcon(GfxContext, arg, toplevel->GetGeometry(), toplevel->GetGeometry()); +} + + +} +} + diff --git a/plugins/unityshell/src/HudIcon.h b/plugins/unityshell/src/HudIcon.h new file mode 100644 index 000000000..5e24b518b --- /dev/null +++ b/plugins/unityshell/src/HudIcon.h @@ -0,0 +1,73 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2010 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: Gord Allott <gord.allott@canonical.com> + * + */ + +#ifndef HUDICON_H +#define HUDICON_H + +#include <set> +#include <string> + +#include "config.h" + +#include <Nux/Nux.h> +#include <Nux/BaseWindow.h> +#include <NuxCore/Math/MathInc.h> + +#include <sigc++/trackable.h> +#include <sigc++/signal.h> +#include <sigc++/functors/ptr_fun.h> +#include <sigc++/functors/mem_fun.h> + +#include <gtk/gtk.h> + +#include "IconTexture.h" +#include "HudIconTextureSource.h" +#include "IconRenderer.h" +#include "Introspectable.h" + +namespace unity +{ +namespace hud +{ + +class Icon : public unity::IconTexture +{ +public: + typedef nux::ObjectPtr<IconTexture> Ptr; + Icon(nux::BaseTexture* texture, guint width, guint height); + Icon(const char* icon_name, unsigned int size, bool defer_icon_loading = false); + ~Icon(); + +protected: + void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); + void Init(); + + nux::ObjectPtr<nux::BaseTexture> background_; + nux::ObjectPtr<nux::BaseTexture> gloss_; + nux::ObjectPtr<nux::BaseTexture> edge_; + nux::ObjectPtr<HudIconTextureSource> icon_texture_source_; + unity::ui::IconRenderer icon_renderer_; +}; + +} + +} + +#endif /* HUDICON_H */ diff --git a/plugins/unityshell/src/HudIconTextureSource.cpp b/plugins/unityshell/src/HudIconTextureSource.cpp new file mode 100644 index 000000000..3e9f06736 --- /dev/null +++ b/plugins/unityshell/src/HudIconTextureSource.cpp @@ -0,0 +1,110 @@ +// -*- 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: Gordon Allott <gord.allott@canonical.com> + */ + + +#include "HudIconTextureSource.h" +#include "config.h" + +#include <glib.h> + +#include <Nux/Nux.h> +#include <NuxCore/Logger.h> + + +namespace unity +{ +namespace hud +{ + +HudIconTextureSource::HudIconTextureSource(nux::ObjectPtr<nux::BaseTexture> texture) + : unity::ui::IconTextureSource() + , icon_texture_(texture) +{ +} + +HudIconTextureSource::~HudIconTextureSource() +{ +} + +void HudIconTextureSource::ColorForIcon(GdkPixbuf* pixbuf) +{ + unsigned int width = gdk_pixbuf_get_width(pixbuf); + unsigned int height = gdk_pixbuf_get_height(pixbuf); + unsigned int row_bytes = gdk_pixbuf_get_rowstride(pixbuf); + + long int rtotal = 0, gtotal = 0, btotal = 0; + float total = 0.0f; + + guchar* img = gdk_pixbuf_get_pixels(pixbuf); + + for (unsigned int i = 0; i < width; i++) + { + for (unsigned int j = 0; j < height; j++) + { + guchar* pixels = img + (j * row_bytes + i * 4); + guchar r = *(pixels + 0); + guchar g = *(pixels + 1); + guchar b = *(pixels + 2); + guchar a = *(pixels + 3); + + float saturation = (MAX(r, MAX(g, b)) - MIN(r, MIN(g, b))) / 255.0f; + float relevance = .1 + .9 * (a / 255.0f) * saturation; + + rtotal += (guchar)(r * relevance); + gtotal += (guchar)(g * relevance); + btotal += (guchar)(b * relevance); + + total += relevance * 255; + } + } + + nux::color::RedGreenBlue rgb(rtotal / total, + gtotal / total, + btotal / total); + nux::color::HueSaturationValue hsv(rgb); + + if (hsv.saturation > 0.15f) + hsv.saturation = 0.65f; + + hsv.value = 0.90f; + bg_color = nux::Color(nux::color::RedGreenBlue(hsv)); +} + +nux::Color HudIconTextureSource::BackgroundColor() +{ + return bg_color; +} + +nux::BaseTexture* HudIconTextureSource::TextureForSize(int size) +{ + return icon_texture_.GetPointer(); +} + +nux::Color HudIconTextureSource::GlowColor() +{ + return nux::Color(0.0f, 0.0f, 0.0f, 0.0f); +} + +nux::BaseTexture* HudIconTextureSource::Emblem() +{ + return nullptr; +} + +} +} \ No newline at end of file diff --git a/plugins/unityshell/src/HudIconTextureSource.h b/plugins/unityshell/src/HudIconTextureSource.h new file mode 100644 index 000000000..e0537f9c2 --- /dev/null +++ b/plugins/unityshell/src/HudIconTextureSource.h @@ -0,0 +1,51 @@ +// -*- 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: Gordon Allott <gord.allott@canonical.com> + */ + + +#ifndef HUDICONTEXTURESOURCE_H +#define HUDICONTEXTURESOURCE_H + +#include "IconTextureSource.h" + +namespace unity +{ +namespace hud +{ + +class HudIconTextureSource : public unity::ui::IconTextureSource +{ +public: + HudIconTextureSource(nux::ObjectPtr<nux::BaseTexture> texture); + ~HudIconTextureSource(); + + virtual nux::Color BackgroundColor(); + virtual nux::BaseTexture* TextureForSize(int size); + virtual nux::Color GlowColor(); + virtual nux::BaseTexture* Emblem(); + void ColorForIcon(GdkPixbuf* pixbuf); + +private: + nux::Color bg_color; + nux::ObjectPtr<nux::BaseTexture> icon_texture_; +}; + +} +} + +#endif // HUDICONTEXTURESOURCE_H diff --git a/plugins/unityshell/src/HudView.cpp b/plugins/unityshell/src/HudView.cpp new file mode 100644 index 000000000..3abbd01c8 --- /dev/null +++ b/plugins/unityshell/src/HudView.cpp @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2010 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: Gord Allott <gord.allott@canonical.com> + */ + +#include "HudView.h" + +#include <math.h> + +#include <gio/gdesktopappinfo.h> +#include <glib/gi18n-lib.h> +#include <gtk/gtk.h> +#include <Nux/Button.h> +#include <iostream> +#include <sstream> + +#include <NuxCore/Logger.h> +#include <UnityCore/GLibWrapper.h> +#include <UnityCore/RadioOptionFilter.h> +#include <Nux/HLayout.h> +#include <Nux/LayeredLayout.h> + +#include <NuxCore/Logger.h> +#include "HudButton.h" +#include "UBusMessages.h" +#include "DashStyle.h" + +namespace unity +{ +namespace hud +{ + +namespace +{ +nux::logging::Logger logger("unity.hud.view"); +int icon_size = 42; +const std::string default_text = _("Type your command"); +} + +NUX_IMPLEMENT_OBJECT_TYPE(View); + +View::View() + : nux::View(NUX_TRACKER_LOCATION) + , button_views_(NULL) +{ + renderer_.SetOwner(this); + renderer_.need_redraw.connect([this] () { + QueueDraw(); + }); + + nux::ROPConfig rop; + rop.Blend = true; + rop.SrcBlend = GL_ONE; + rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; + + SetupViews(); + search_bar_->key_down.connect (sigc::mem_fun (this, &View::OnKeyDown)); + + search_bar_->activated.connect ([&]() { + search_activated.emit(search_bar_->search_string); + }); + + mouse_down.connect(sigc::mem_fun(this, &View::OnMouseButtonDown)); + + Relayout(); + +} + +View::~View() +{ + RemoveChild(search_bar_.GetPointer()); + for (auto button = buttons_.begin(); button != buttons_.end(); button++) + { + RemoveChild((*button).GetPointer()); + } +} + +void View::ResetToDefault() +{ + search_bar_->search_string = ""; + search_bar_->search_hint = default_text; +} + +void View::Relayout() +{ + nux::Geometry geo = GetGeometry(); + content_geo_ = GetBestFitGeometry(geo); + LOG_DEBUG(logger) << "content_geo: " << content_geo_.width << "x" << content_geo_.height; + + layout_->SetMinMaxSize(content_geo_.width, content_geo_.height); + + QueueDraw(); +} + +long View::PostLayoutManagement(long LayoutResult) +{ + Relayout(); + return LayoutResult; +} + + +nux::View* View::default_focus() const +{ + return search_bar_->text_entry(); +} + +void View::SetQueries(Hud::Queries queries) +{ + // remove the previous children + for (auto button = buttons_.begin(); button != buttons_.end(); button++) + { + RemoveChild((*button).GetPointer()); + } + + queries_ = queries_; + buttons_.clear(); + button_views_->Clear(); + int found_items = 0; + for (auto query = queries.begin(); query != queries.end(); query++) + { + if (found_items > 5) + break; + + HudButton::Ptr button = HudButton::Ptr(new HudButton()); + buttons_.push_front(button); + button->SetQuery(*query); + button_views_->AddView(button.GetPointer(), 0, nux::MINOR_POSITION_LEFT); + + button->click.connect([&](nux::View* view) { + query_activated.emit(dynamic_cast<HudButton*>(view)->GetQuery()); + }); + + button->key_nav_focus_activate.connect([&](nux::Area *area) { + query_activated.emit(dynamic_cast<HudButton*>(area)->GetQuery()); + }); + + button->key_nav_focus_change.connect([&](nux::Area *area, bool recieving, KeyNavDirection direction){ + if (recieving) + query_selected.emit(dynamic_cast<HudButton*>(area)->GetQuery()); + }); + + button->is_rounded = (query == --(queries.end())) ? true : false; + button->SetMinimumWidth(941); + found_items++; + } + + QueueRelayout(); + QueueDraw(); +} + +void View::SetIcon(std::string icon_name) +{ + LOG_DEBUG(logger) << "Setting icon to " << icon_name; + icon_->SetByIconName(icon_name.c_str(), icon_size); + QueueDraw(); +} + +// Gives us the width and height of the contents that will give us the best "fit", +// which means that the icons/views will not have uneccessary padding, everything will +// look tight +nux::Geometry View::GetBestFitGeometry(nux::Geometry const& for_geo) +{ + //FIXME - remove magic values, replace with scalable text depending on DPI + // requires smarter font settings really... + int width, height = 0; + width = 1024; + height = 276; + + LOG_DEBUG (logger) << "best fit is, " << width << ", " << height; + + return nux::Geometry(0, 0, width, height); +} + +void View::AboutToShow() +{ + renderer_.AboutToShow(); +} + +void View::AboutToHide() +{ + renderer_.AboutToHide(); +} + +void View::SetWindowGeometry(nux::Geometry const& absolute_geo, nux::Geometry const& geo) +{ + window_geometry_ = geo; + window_geometry_.x = 0; + window_geometry_.y = 0; + absolute_window_geometry_ = absolute_geo; +} + +namespace +{ + const int top_spacing = 9; + const int content_width = 941; + const int icon_vertical_margin = 5; + const int spacing_between_icon_and_content = 8; +} + +void View::SetupViews() +{ + layout_ = new nux::HLayout(); + + icon_ = new Icon("", icon_size, true); + nux::Layout* icon_layout = new nux::VLayout(); + icon_layout->SetVerticalExternalMargin(icon_vertical_margin); + icon_layout->AddView(icon_.GetPointer(), 0, nux::MINOR_POSITION_LEFT, nux::MINOR_SIZE_FULL); + layout_->AddLayout(icon_layout, 0, nux::MINOR_POSITION_TOP, nux::MINOR_SIZE_MATCHCONTENT); + layout_->AddLayout(new nux::SpaceLayout(spacing_between_icon_and_content, + spacing_between_icon_and_content, + spacing_between_icon_and_content, + spacing_between_icon_and_content), 0); + + + content_layout_ = new nux::VLayout(); + layout_->AddLayout(content_layout_.GetPointer(), 1, nux::MINOR_POSITION_TOP); + SetLayout(layout_.GetPointer()); + + // add the top spacing + content_layout_->AddLayout(new nux::SpaceLayout(top_spacing,top_spacing,top_spacing,top_spacing), 0); + + // add the search bar to the composite + search_bar_ = new unity::SearchBar(content_width, true); + search_bar_->disable_glow = true; + search_bar_->search_hint = default_text; + search_bar_->search_changed.connect(sigc::mem_fun(this, &View::OnSearchChanged)); + AddChild(search_bar_.GetPointer()); + content_layout_->AddView(search_bar_.GetPointer(), 0, nux::MINOR_POSITION_LEFT); + + button_views_ = new nux::VLayout(); + button_views_->SetMaximumWidth(content_width); + + content_layout_->AddLayout(button_views_.GetPointer(), 1, nux::MINOR_POSITION_LEFT); +} + +void View::OnSearchChanged(std::string const& search_string) +{ + LOG_DEBUG(logger) << "got search change"; + search_changed.emit(search_string); + if (search_string.empty()) + { + search_bar_->search_hint = default_text; + } + else + { + search_bar_->search_hint = ""; + } +} + + +void View::OnKeyDown (unsigned long event_type, unsigned long keysym, + unsigned long event_state, const TCHAR* character, + unsigned short key_repeat_count) +{ + if (keysym == NUX_VK_ESCAPE) + { + LOG_DEBUG(logger) << "got escape key"; + ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST); + } +} + +void View::OnMouseButtonDown(int x, int y, unsigned long button, unsigned long key) +{ + if (!content_geo_.IsPointInside(x, y)) + { + ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST); + } +} + +void View::Draw(nux::GraphicsEngine& gfx_context, bool force_draw) +{ + renderer_.DrawFull(gfx_context, layout_->GetGeometry(), absolute_window_geometry_, window_geometry_, true); +} + +void View::DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw) +{ + renderer_.DrawInner(gfx_context, layout_->GetGeometry(), absolute_window_geometry_, window_geometry_); + + if (IsFullRedraw()) + { + nux::GetPainter().PushBackgroundStack(); + layout_->ProcessDraw(gfx_context, force_draw); + nux::GetPainter().PopBackgroundStack(); + } + else + { + layout_->ProcessDraw(gfx_context, force_draw); + } + + renderer_.DrawInnerCleanup(gfx_context, layout_->GetGeometry(), absolute_window_geometry_, window_geometry_); +} + +// Keyboard navigation +bool View::AcceptKeyNavFocus() +{ + return false; +} + +// Introspectable +std::string View::GetName() const +{ + return "HudView"; +} + +void View::AddProperties(GVariantBuilder* builder) +{ + +} + +bool View::InspectKeyEvent(unsigned int eventType, + unsigned int key_sym, + const char* character) +{ + if ((eventType == nux::NUX_KEYDOWN) && (key_sym == NUX_VK_ESCAPE)) + { + if (search_bar_->search_string == "") + { + ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST); + } + else + { + search_bar_->search_string = ""; + search_bar_->search_hint = default_text; + } + return true; + } + return false; +} + +nux::Area* View::FindKeyFocusArea(unsigned int key_symbol, + unsigned long x11_key_code, + unsigned long special_keys_state) +{ + // Do what nux::View does, but if the event isn't a key navigation, + // designate the text entry to process it. + + nux::KeyNavDirection direction = nux::KEY_NAV_NONE; + switch (x11_key_code) + { + case NUX_VK_UP: + direction = nux::KEY_NAV_UP; + break; + case NUX_VK_DOWN: + direction = nux::KEY_NAV_DOWN; + break; + case NUX_VK_LEFT: + direction = nux::KEY_NAV_LEFT; + break; + case NUX_VK_RIGHT: + direction = nux::KEY_NAV_RIGHT; + break; + case NUX_VK_LEFT_TAB: + direction = nux::KEY_NAV_TAB_PREVIOUS; + break; + case NUX_VK_TAB: + direction = nux::KEY_NAV_TAB_NEXT; + break; + case NUX_VK_ENTER: + case NUX_KP_ENTER: + // Not sure if Enter should be a navigation key + direction = nux::KEY_NAV_ENTER; + break; + default: + direction = nux::KEY_NAV_NONE; + break; + } + + if (has_key_focus_) + { + return this; + } + else if (direction == nux::KEY_NAV_NONE) + { + // then send the event to the search entry + return search_bar_->text_entry(); + } + else if (next_object_to_key_focus_area_) + { + return next_object_to_key_focus_area_->FindKeyFocusArea(key_symbol, x11_key_code, special_keys_state); + } + return NULL; +} + +} +} diff --git a/plugins/unityshell/src/HudView.h b/plugins/unityshell/src/HudView.h new file mode 100644 index 000000000..b0a54d740 --- /dev/null +++ b/plugins/unityshell/src/HudView.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2010 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: Gordon Allott <gord.allott@canonical.com> + */ + +#ifndef UNITY_HUD_VIEW_H_ +#define UNITY_HUD_VIEW_H_ + +#include <string> + +#include <NuxGraphics/GraphicsEngine.h> +#include <Nux/Nux.h> +#include <Nux/PaintLayer.h> +#include <Nux/View.h> +#include <Nux/VLayout.h> +#include <StaticCairoText.h> + +#include <glib.h> + +#include <UnityCore/Hud.h> +#include "Introspectable.h" + +#include "UBusWrapper.h" +#include "HudIcon.h" +#include "HudButton.h" +#include "SearchBar.h" +#include "OverlayRenderer.h" + +namespace unity +{ +namespace hud +{ + +class View : public nux::View, public unity::debug::Introspectable +{ + NUX_DECLARE_OBJECT_TYPE(HudView, nux::View); + typedef nux::ObjectPtr<View> Ptr; +public: + View(); + ~View(); + + void ResetToDefault(); + + void Relayout(); + nux::View* default_focus() const; + + void SetQueries(Hud::Queries queries); + void SetIcon(std::string icon_name); + + void AboutToShow(); + void AboutToHide(); + + void SetWindowGeometry(nux::Geometry const& absolute_geo, nux::Geometry const& geo); + + sigc::signal<void, std::string> search_changed; + sigc::signal<void, std::string> search_activated; + sigc::signal<void, Query::Ptr> query_activated; + sigc::signal<void, Query::Ptr> query_selected; + +protected: + virtual Area* FindKeyFocusArea(unsigned int key_symbol, + unsigned long x11_key_code, + unsigned long special_keys_state); + + void SetupViews(); + void OnSearchChanged(std::string const& search_string); + virtual long PostLayoutManagement(long LayoutResult); +private: + void OnMouseButtonDown(int x, int y, unsigned long button, unsigned long key); + void OnKeyDown (unsigned long event_type, unsigned long event_keysym, + unsigned long event_state, const TCHAR* character, + unsigned short key_repeat_count); + void Draw(nux::GraphicsEngine& gfx_context, bool force_draw); + void DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw); + bool InspectKeyEvent(unsigned int eventType, unsigned int key_sym, const char* character); + bool AcceptKeyNavFocus(); + nux::Geometry GetBestFitGeometry(nux::Geometry const& for_geo); + + std::string GetName() const; + void AddProperties(GVariantBuilder* builder); + +private: + UBusManager ubus; + nux::ObjectPtr<nux::Layout> layout_; + nux::ObjectPtr<nux::Layout> content_layout_; + nux::ObjectPtr<nux::VLayout> button_views_; + std::list<HudButton::Ptr> buttons_; + + //FIXME - replace with dash search bar once modifications to dash search bar land + SearchBar::Ptr search_bar_; + Icon::Ptr icon_; + bool visible_; + + Hud::Queries queries_; + nux::Geometry content_geo_; + OverlayRenderer renderer_; + nux::Geometry window_geometry_; + nux::Geometry absolute_window_geometry_; +}; + + +} +} +#endif + diff --git a/plugins/unityshell/src/IMTextEntry.cpp b/plugins/unityshell/src/IMTextEntry.cpp index 1704a5561..bc1b217c6 100644 --- a/plugins/unityshell/src/IMTextEntry.cpp +++ b/plugins/unityshell/src/IMTextEntry.cpp @@ -27,12 +27,10 @@ namespace unity { -namespace dash -{ namespace { -nux::logging::Logger logger("unity.dash.imtextentry"); +nux::logging::Logger logger("unity.imtextentry"); } NUX_IMPLEMENT_OBJECT_TYPE(IMTextEntry); @@ -338,4 +336,3 @@ void IMTextEntry::OnMouseButtonUp(int x, int y, unsigned long bflags, unsigned l } } -} diff --git a/plugins/unityshell/src/IMTextEntry.h b/plugins/unityshell/src/IMTextEntry.h index 9209c84f5..ab847bae6 100644 --- a/plugins/unityshell/src/IMTextEntry.h +++ b/plugins/unityshell/src/IMTextEntry.h @@ -30,8 +30,6 @@ namespace unity { -namespace dash -{ using namespace unity::glib; using namespace nux; @@ -80,6 +78,5 @@ private: }; } -} #endif diff --git a/plugins/unityshell/src/IconTexture.cpp b/plugins/unityshell/src/IconTexture.cpp index 24a9910ea..0741e63e6 100644 --- a/plugins/unityshell/src/IconTexture.cpp +++ b/plugins/unityshell/src/IconTexture.cpp @@ -24,6 +24,7 @@ #include <pango/pangocairo.h> #include <Nux/Nux.h> +#include <NuxCore/Logger.h> #include <NuxGraphics/GLThread.h> #include <UnityCore/GLibWrapper.h> #include <UnityCore/Variant.h> @@ -37,6 +38,7 @@ namespace unity namespace { const char* const DEFAULT_ICON = "text-x-preview"; +nux::logging::Logger logger("unity.icontexture"); } using namespace unity; @@ -67,7 +69,7 @@ IconTexture::IconTexture(const char* icon_name, unsigned int size, bool defer_ic { _icon_name = g_strdup(icon_name ? icon_name : DEFAULT_ICON); - if (!g_strcmp0(_icon_name, "") == 0 && !defer_icon_loading) + if (g_strcmp0(_icon_name, "") != 0 && !defer_icon_loading) LoadIcon(); } @@ -95,8 +97,11 @@ void IconTexture::SetByFilePath(const char* file_path, unsigned int size) void IconTexture::LoadIcon() { - static const char* const DEFAULT_GICON = ". GThemedIcon text-x-preview"; - + LOG_DEBUG(logger) << "LoadIcon called (" << _icon_name << ") - loading: " << _loading; +static const char* const DEFAULT_GICON = ". GThemedIcon text-x-preview"; + if (!g_strcmp0(_icon_name, "")) + return; + if (_loading) return; _loading = true; @@ -144,6 +149,7 @@ void IconTexture::Refresh(GdkPixbuf* pixbuf) _texture_height, sigc::mem_fun(this, &IconTexture::CreateTextureCallback)); QueueDraw(); + _loading = false; } void IconTexture::IconLoaded(std::string const& icon_name, unsigned size, @@ -162,6 +168,9 @@ void IconTexture::IconLoaded(std::string const& icon_name, unsigned size, if (icon_name != DEFAULT_ICON) SetByIconName(DEFAULT_ICON, _size); } + + texture_updated.emit(_texture_cached.GetPointer()); + QueueDraw(); } void IconTexture::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) diff --git a/plugins/unityshell/src/IconTexture.h b/plugins/unityshell/src/IconTexture.h index 57a0aa4f9..7bfdc1a64 100644 --- a/plugins/unityshell/src/IconTexture.h +++ b/plugins/unityshell/src/IconTexture.h @@ -53,6 +53,8 @@ public: nux::BaseTexture* texture(); + sigc::signal<void, nux::BaseTexture*> texture_updated; + protected: // Key navigation virtual bool AcceptKeyNavFocus(); @@ -61,7 +63,8 @@ protected: std::string GetName() const; void AddProperties(GVariantBuilder* builder); virtual bool DoCanFocus(); - + GdkPixbuf* _pixbuf_cached; + protected: void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); @@ -74,7 +77,7 @@ private: char* _icon_name; unsigned int _size; - GdkPixbuf* _pixbuf_cached; + nux::ObjectPtr<nux::BaseTexture> _texture_cached; // FIXME: make these two a nux::Size. int _texture_width; diff --git a/plugins/unityshell/src/Introspectable.h b/plugins/unityshell/src/Introspectable.h index d7a5022fd..baa40fde9 100644 --- a/plugins/unityshell/src/Introspectable.h +++ b/plugins/unityshell/src/Introspectable.h @@ -52,13 +52,8 @@ protected: * AddProperties should be implemented as such ... * void ClassFoo::AddProperties (GVariantBuilder *builder) * { - * g_variant_builder_add (builder, "{sv}", "label", g_variant_new_string ("_File") ); - * g_variant_builder_add (builder, "{sv}", "image", g_variant_new_string ("") ); - * g_variant_builder_add (builder, "{sv}", "visible", g_variant_new_boolean (TRUE) ); - * g_variant_builder_add (builder, "{sv}", "sensitive", g_variant_new_boolean (TRUE) ); - * g_variant_builder_add (builder, "{sv}", "active", g_variant_new_boolean (FALSE) ); - * g_variant_builder_add (builder, "{sv}", "label", g_variant_new_int32 (34) ); - * g_variant_builder_add (builder, "{sv}", "label", g_variant_new_int32 (24) ); + * unity::variant::BuilderWrapper wrapper(builder); + * wrapper.add("label", some_value); * } * That's all. Just add a bunch of key-value properties to the builder. */ diff --git a/plugins/unityshell/src/Launcher.cpp b/plugins/unityshell/src/Launcher.cpp index 1fc19b935..70b79f1ae 100644 --- a/plugins/unityshell/src/Launcher.cpp +++ b/plugins/unityshell/src/Launcher.cpp @@ -411,7 +411,8 @@ Launcher::AddProperties(GVariantBuilder* builder) .add("monitor", monitor()) .add("quicklist-open", _hide_machine->GetQuirk(LauncherHideMachine::QUICKLIST_OPEN)) .add("hide-quirks", _hide_machine->DebugHideQuirks().c_str()) - .add("hover-quirks", _hover_machine->DebugHoverQuirks().c_str()); + .add("hover-quirks", _hover_machine->DebugHoverQuirks().c_str()) + .add("icon-size", _icon_size); } void Launcher::SetMousePosition(int x, int y) diff --git a/plugins/unityshell/src/LauncherController.cpp b/plugins/unityshell/src/LauncherController.cpp index 4a59590b7..fdc64ef65 100644 --- a/plugins/unityshell/src/LauncherController.cpp +++ b/plugins/unityshell/src/LauncherController.cpp @@ -273,7 +273,8 @@ void Controller::Impl::OnScreenChanged(int primary_monitor, std::vector<nux::Geo { unsigned int num_monitors = monitors.size(); - for (unsigned int i = 0; i < num_monitors; i++) + unsigned int i; + for (i = 0; i < num_monitors; i++) { if (i >= launchers.size()) launchers.push_back(nux::ObjectPtr<Launcher> (CreateLauncher(i))); @@ -281,6 +282,12 @@ void Controller::Impl::OnScreenChanged(int primary_monitor, std::vector<nux::Geo launchers[i]->Resize(); } + for (; i < launchers.size(); ++i) + { + auto launcher = launchers[i]; + if (launcher.IsValid()) + launcher->GetParent()->UnReference(); + } launchers.resize(num_monitors); } diff --git a/plugins/unityshell/src/LensBar.cpp b/plugins/unityshell/src/LensBar.cpp index c59fdb683..d28d0d0e0 100644 --- a/plugins/unityshell/src/LensBar.cpp +++ b/plugins/unityshell/src/LensBar.cpp @@ -220,7 +220,15 @@ std::string LensBar::GetName() const } void LensBar::AddProperties(GVariantBuilder* builder) -{} +{ + unity::variant::BuilderWrapper wrapper(builder); + for( auto icon : icons_) + { + if (icon->active) + wrapper.add("active-lens", icon->id.Get()); + } + +} } } diff --git a/plugins/unityshell/src/OverlayRenderer.cpp b/plugins/unityshell/src/OverlayRenderer.cpp index 1ca54b8b1..88cde19f5 100644 --- a/plugins/unityshell/src/OverlayRenderer.cpp +++ b/plugins/unityshell/src/OverlayRenderer.cpp @@ -50,7 +50,7 @@ public: void Init(); void OnBackgroundColorChanged(GVariant* args); - void Draw(nux::GraphicsEngine& gfx_context, nux::Geometry content_geo, nux::Geometry absolute_geo, nux::Geometry geometry); + void Draw(nux::GraphicsEngine& gfx_context, nux::Geometry content_geo, nux::Geometry absolute_geo, nux::Geometry geometry, bool force_draw); void DrawContent(nux::GraphicsEngine& gfx_context, nux::Geometry content_geo, nux::Geometry absolute_geo, nux::Geometry geometry); void DrawContentCleanup(nux::GraphicsEngine& gfx_context, nux::Geometry content_geo, nux::Geometry absolute_geo, nux::Geometry geometry); @@ -118,12 +118,12 @@ void OverlayRendererImpl::OnBackgroundColorChanged(GVariant* args) parent->need_redraw.emit(); } -void OverlayRendererImpl::Draw(nux::GraphicsEngine& gfx_context, nux::Geometry content_geo, nux::Geometry absolute_geo, nux::Geometry geometry) +void OverlayRendererImpl::Draw(nux::GraphicsEngine& gfx_context, nux::Geometry content_geo, nux::Geometry absolute_geo, nux::Geometry geometry, bool force_edges) { bool paint_blur = BackgroundEffectHelper::blur_type != BLUR_NONE; nux::Geometry geo = content_geo; - if (dash::Settings::Instance().GetFormFactor() != dash::FormFactor::NETBOOK) + if (dash::Settings::Instance().GetFormFactor() != dash::FormFactor::NETBOOK || force_edges) { // Paint the edges { @@ -475,9 +475,9 @@ void OverlayRenderer::DisableBlur() pimpl_->bg_effect_helper_.blur_type = BLUR_NONE; } -void OverlayRenderer::DrawFull(nux::GraphicsEngine& gfx_context, nux::Geometry content_geo, nux::Geometry absolute_geo, nux::Geometry geo) +void OverlayRenderer::DrawFull(nux::GraphicsEngine& gfx_context, nux::Geometry content_geo, nux::Geometry absolute_geo, nux::Geometry geo, bool force_edges) { - pimpl_->Draw(gfx_context, content_geo, absolute_geo, geo); + pimpl_->Draw(gfx_context, content_geo, absolute_geo, geo, force_edges); } void OverlayRenderer::DrawInner(nux::GraphicsEngine& gfx_context, nux::Geometry content_geo, nux::Geometry absolute_geo, nux::Geometry geo) diff --git a/plugins/unityshell/src/OverlayRenderer.h b/plugins/unityshell/src/OverlayRenderer.h index f4bd624f1..25290f5e5 100644 --- a/plugins/unityshell/src/OverlayRenderer.h +++ b/plugins/unityshell/src/OverlayRenderer.h @@ -70,7 +70,7 @@ public: * absolute_geo: your views GetAbsoluteGeometry() * geo: your views GetGeometry() */ - void DrawFull(nux::GraphicsEngine& gfx_context, nux::Geometry content_geo, nux::Geometry absolute_geo, nux::Geometry geo); + void DrawFull(nux::GraphicsEngine& gfx_context, nux::Geometry content_geo, nux::Geometry absolute_geo, nux::Geometry geo, bool force_edges=false); /* * Draws just the stack that is overlay behind the inner_geometry using push/pop layers, call in DrawContent() before drawing your content diff --git a/plugins/unityshell/src/PanelTray.cpp b/plugins/unityshell/src/PanelTray.cpp index 562ff3e4c..6900a4cd7 100644 --- a/plugins/unityshell/src/PanelTray.cpp +++ b/plugins/unityshell/src/PanelTray.cpp @@ -95,7 +95,6 @@ PanelTray::~PanelTray() if (_tray) { g_signal_handler_disconnect(na_tray_get_manager(_tray), _tray_icon_added_id); - g_object_unref (_tray); _tray = NULL; } @@ -104,9 +103,7 @@ PanelTray::~PanelTray() if (_tray_expose_id) g_signal_handler_disconnect(_window, _tray_expose_id); - // DISABLED to see if we can get compiz to cleanly exit. - // This currently blocks on X. - // gtk_widget_destroy(_window); + gtk_widget_destroy(_window); g_strfreev(_whitelist); g_object_unref(_settings); } diff --git a/plugins/unityshell/src/PointerBarrier.cpp b/plugins/unityshell/src/PointerBarrier.cpp index 81fae0be9..ddfaf52b5 100644 --- a/plugins/unityshell/src/PointerBarrier.cpp +++ b/plugins/unityshell/src/PointerBarrier.cpp @@ -48,6 +48,11 @@ PointerBarrierWrapper::PointerBarrierWrapper() max_velocity_multiplier = 1.0f; } +PointerBarrierWrapper::~PointerBarrierWrapper() +{ + DestroyBarrier(); +} + void PointerBarrierWrapper::ConstructBarrier() { if (active) diff --git a/plugins/unityshell/src/PointerBarrier.h b/plugins/unityshell/src/PointerBarrier.h index 10af894ec..9ebde0918 100644 --- a/plugins/unityshell/src/PointerBarrier.h +++ b/plugins/unityshell/src/PointerBarrier.h @@ -60,6 +60,7 @@ public: nux::Property<float> max_velocity_multiplier; PointerBarrierWrapper(); + ~PointerBarrierWrapper(); void ConstructBarrier(); void DestroyBarrier(); diff --git a/plugins/unityshell/src/QuicklistMenuItemCheckmark.cpp b/plugins/unityshell/src/QuicklistMenuItemCheckmark.cpp index b6f4455c9..3b2686b25 100644 --- a/plugins/unityshell/src/QuicklistMenuItemCheckmark.cpp +++ b/plugins/unityshell/src/QuicklistMenuItemCheckmark.cpp @@ -110,13 +110,12 @@ QuicklistMenuItemCheckmark::PostLayoutManagement(long layoutResult) } void -QuicklistMenuItemCheckmark::Draw(nux::GraphicsEngine& gfxContext, - bool forceDraw) +QuicklistMenuItemCheckmark::Draw(nux::GraphicsEngine& gfxContext, bool forceDraw) { nux::ObjectPtr<nux::IOpenGLBaseTexture> texture; // Check if the texture have been computed. If they haven't, exit the function. - if (!_normalTexture[0]) + if (!_normalTexture[0] || !_prelightTexture[0]) return; nux::Geometry base = GetGeometry(); @@ -130,42 +129,19 @@ QuicklistMenuItemCheckmark::Draw(nux::GraphicsEngine& gfxContext, gfxContext.GetRenderStates().SetBlend(true); gfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER); - if (GetEnabled()) + unsigned int texture_idx = GetActive() ? 1 : 0; + + if (!_prelight || !GetEnabled()) { - if (GetActive() && _prelight) - { - texture = _prelightTexture[0]->GetDeviceTexture(); - } - else if (GetActive()) - { - texture = _normalTexture[0]->GetDeviceTexture(); - } - - if ((!GetActive()) && _prelight) - { - texture = _prelightTexture[1]->GetDeviceTexture(); - } - else if (!GetActive()) - { - texture = _normalTexture[1]->GetDeviceTexture(); - } - - _color = nux::color::White; + texture = _normalTexture[texture_idx]->GetDeviceTexture(); } else { - if (GetActive()) - { - texture = _prelightTexture[0]->GetDeviceTexture(); - } - else - { - texture = _normalTexture[0]->GetDeviceTexture(); - } - - _color = nux::Color(0.8f, 0.8f, 0.8f, 1.0f); + texture = _prelightTexture[texture_idx]->GetDeviceTexture(); } + _color = GetEnabled() ? nux::color::White : nux::Color(0.8f, 0.8f, 0.8f, 1.0f); + gfxContext.QRP_1Tex(base.x, base.y, base.width, diff --git a/plugins/unityshell/src/QuicklistMenuItemRadio.cpp b/plugins/unityshell/src/QuicklistMenuItemRadio.cpp index c4b8c164e..2092c40d5 100644 --- a/plugins/unityshell/src/QuicklistMenuItemRadio.cpp +++ b/plugins/unityshell/src/QuicklistMenuItemRadio.cpp @@ -109,15 +109,14 @@ QuicklistMenuItemRadio::PostLayoutManagement(long layoutResult) } void -QuicklistMenuItemRadio::Draw(nux::GraphicsEngine& gfxContext, - bool forceDraw) +QuicklistMenuItemRadio::Draw(nux::GraphicsEngine& gfxContext, bool forceDraw) { + nux::ObjectPtr<nux::IOpenGLBaseTexture> texture; + // Check if the texture have been computed. If they haven't, exit the function. - if (!_normalTexture[0]) + if (!_normalTexture[0] || !_prelightTexture[0]) return; - nux::ObjectPtr<nux::IOpenGLBaseTexture> texture; - nux::Geometry base = GetGeometry(); gfxContext.PushClippingRectangle(base); @@ -129,42 +128,19 @@ QuicklistMenuItemRadio::Draw(nux::GraphicsEngine& gfxContext, gfxContext.GetRenderStates().SetBlend(true); gfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER); - if (GetEnabled()) + unsigned int texture_idx = GetActive() ? 1 : 0; + + if (!_prelight || !GetEnabled()) { - if (GetActive() && _prelight) - { - texture = _prelightTexture[0]->GetDeviceTexture(); - } - else if (GetActive()) - { - texture = _normalTexture[0]->GetDeviceTexture(); - } - - if ((!GetActive()) && _prelight) - { - texture = _prelightTexture[1]->GetDeviceTexture(); - } - else if (!GetActive()) - { - texture = _normalTexture[1]->GetDeviceTexture(); - } - - _color = nux::color::White; + texture = _normalTexture[texture_idx]->GetDeviceTexture(); } else { - if (GetActive()) - { - texture = _prelightTexture[0]->GetDeviceTexture(); - } - else - { - texture = _normalTexture[0]->GetDeviceTexture(); - } - - _color = nux::Color(0.8f, 0.8f, 0.8f, 1.0f); + texture = _prelightTexture[texture_idx]->GetDeviceTexture(); } + _color = GetEnabled() ? nux::color::White : nux::Color(0.8f, 0.8f, 0.8f, 1.0f); + gfxContext.QRP_1Tex(base.x, base.y, base.width, diff --git a/plugins/unityshell/src/QuicklistView.cpp b/plugins/unityshell/src/QuicklistView.cpp index 98a9e2c1c..4003b1a06 100644 --- a/plugins/unityshell/src/QuicklistView.cpp +++ b/plugins/unityshell/src/QuicklistView.cpp @@ -905,8 +905,7 @@ QuicklistMenuItemType QuicklistView::GetNthType(int index) std::list<QuicklistMenuItem*> QuicklistView::GetChildren() { - std::list<QuicklistMenuItem*> l; - return l; + return _item_list; } void QuicklistView::DefaultToFirstItem() diff --git a/plugins/unityshell/src/DashSearchBar.cpp b/plugins/unityshell/src/SearchBar.cpp index 090d70ebc..a72647ae6 100644 --- a/plugins/unityshell/src/DashSearchBar.cpp +++ b/plugins/unityshell/src/SearchBar.cpp @@ -25,6 +25,7 @@ #include <Nux/VLayout.h> #include <Nux/Layout.h> #include <Nux/WindowCompositor.h> +#include <NuxCore/Logger.h> #include <NuxImage/CairoGraphics.h> #include <NuxImage/ImageSurface.h> @@ -36,7 +37,7 @@ #include <glib.h> #include <glib/gi18n-lib.h> -#include "DashSearchBar.h" +#include "SearchBar.h" #include <UnityCore/Variant.h> #include "CairoTexture.h" @@ -48,11 +49,16 @@ namespace { const float kExpandDefaultIconOpacity = 1.0f; +const int external_margin_vertical = 8; +const int external_margin_horizontal = 7; } -namespace unity +namespace { -namespace dash + nux::logging::Logger logger("unity"); +} + +namespace unity { NUX_IMPLEMENT_OBJECT_TYPE(SearchBar); @@ -62,18 +68,53 @@ SearchBar::SearchBar(NUX_FILE_LINE_DECL) , search_hint("") , showing_filters(false) , can_refine_search(false) + , disable_glow(false) + , show_filter_hint_(true) , search_bar_width_(642) , live_search_timeout_(0) , start_spinner_timeout_(0) { + Init(); +} + +SearchBar::SearchBar(int search_bar_width, bool show_filter_hint_, NUX_FILE_LINE_DECL) + : View(NUX_FILE_LINE_PARAM) + , search_hint("") + , showing_filters(false) + , can_refine_search(false) + , disable_glow(false) + , show_filter_hint_(show_filter_hint_) + , search_bar_width_(search_bar_width) + , live_search_timeout_(0) + , start_spinner_timeout_(0) +{ + Init(); +} + +SearchBar::SearchBar(int search_bar_width, NUX_FILE_LINE_DECL) + : View(NUX_FILE_LINE_PARAM) + , search_hint("") + , showing_filters(false) + , can_refine_search(false) + , disable_glow(false) + , show_filter_hint_(true) + , search_bar_width_(search_bar_width) + , live_search_timeout_(0) + , start_spinner_timeout_(0) +{ + Init(); +} + +void SearchBar::Init() +{ nux::BaseTexture* icon = dash::Style::Instance().GetSearchMagnifyIcon(); bg_layer_ = new nux::ColorLayer(nux::Color(0xff595853), true); layout_ = new nux::HLayout(NUX_TRACKER_LOCATION); layout_->SetHorizontalInternalMargin(0); - layout_->SetVerticalExternalMargin(8); - layout_->SetHorizontalExternalMargin(7); + layout_->SetVerticalExternalMargin(external_margin_vertical); + layout_->SetHorizontalExternalMargin(external_margin_horizontal); SetLayout(layout_); spinner_ = new SearchBarSpinner(); @@ -102,42 +143,44 @@ SearchBar::SearchBar(NUX_FILE_LINE_DECL) layered_layout_->SetMaximumWidth(search_bar_width_); layout_->AddView(layered_layout_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FIX); - std::string filter_str = _("<small><b>Filter results</b></small>"); - show_filters_ = new nux::StaticCairoText(filter_str.c_str()); - show_filters_->SetVisible(false); - show_filters_->SetFont("Ubuntu 10"); - show_filters_->SetTextColor(nux::Color(1.0f, 1.0f, 1.0f, 1.0f)); - show_filters_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_LEFT); - show_filters_->mouse_click.connect([&] (int x, int y, unsigned long b, unsigned long k) { showing_filters = !showing_filters; }); - - nux::BaseTexture* arrow; - arrow = dash::Style::Instance().GetGroupExpandIcon(); - expand_icon_ = new IconTexture(arrow, - arrow->GetWidth(), - arrow->GetHeight()); - expand_icon_->SetOpacity(kExpandDefaultIconOpacity); - expand_icon_->SetMinimumSize(arrow->GetWidth(), arrow->GetHeight()); - expand_icon_->SetVisible(false); - expand_icon_->mouse_click.connect([&] (int x, int y, unsigned long b, unsigned long k) { showing_filters = !showing_filters; }); - - filter_layout_ = new nux::HLayout(); - filter_layout_->SetHorizontalInternalMargin(8); - filter_layout_->SetHorizontalExternalMargin(6); - filter_space_ = new nux::SpaceLayout(100, 10000, 0, 1); - filter_layout_->AddLayout(filter_space_, 1); - filter_layout_->AddView(show_filters_, 0, nux::MINOR_POSITION_CENTER); - - arrow_layout_ = new nux::VLayout(); - arrow_top_space_ = new nux::SpaceLayout(2, 2, 12, 12); - arrow_bottom_space_ = new nux::SpaceLayout(2, 2, 8, 8); - arrow_layout_->AddView(arrow_top_space_, 0, nux::MINOR_POSITION_CENTER); - arrow_layout_->AddView(expand_icon_, 0, nux::MINOR_POSITION_CENTER); - arrow_layout_->AddView(arrow_bottom_space_, 0, nux::MINOR_POSITION_CENTER); - - filter_layout_->AddView(arrow_layout_, 0, nux::MINOR_POSITION_CENTER); - - layout_->AddView(filter_layout_, 1, nux::MINOR_POSITION_RIGHT, nux::MINOR_SIZE_FULL); - + if (show_filter_hint_) + { + std::string filter_str = _("<small><b>Filter results</b></small>"); + show_filters_ = new nux::StaticCairoText(filter_str.c_str()); + show_filters_->SetVisible(false); + show_filters_->SetFont("Ubuntu 10"); + show_filters_->SetTextColor(nux::Color(1.0f, 1.0f, 1.0f, 1.0f)); + show_filters_->SetTextAlignment(nux::StaticCairoText::NUX_ALIGN_LEFT); + show_filters_->mouse_click.connect([&] (int x, int y, unsigned long b, unsigned long k) { showing_filters = !showing_filters; }); + + nux::BaseTexture* arrow; + arrow = dash::Style::Instance().GetGroupExpandIcon(); + expand_icon_ = new IconTexture(arrow, + arrow->GetWidth(), + arrow->GetHeight()); + expand_icon_->SetOpacity(kExpandDefaultIconOpacity); + expand_icon_->SetMinimumSize(arrow->GetWidth(), arrow->GetHeight()); + expand_icon_->SetVisible(false); + expand_icon_->mouse_click.connect([&] (int x, int y, unsigned long b, unsigned long k) { showing_filters = !showing_filters; }); + + filter_layout_ = new nux::HLayout(); + filter_layout_->SetHorizontalInternalMargin(8); + filter_layout_->SetHorizontalExternalMargin(6); + filter_space_ = new nux::SpaceLayout(100, 10000, 0, 1); + filter_layout_->AddLayout(filter_space_, 1); + filter_layout_->AddView(show_filters_, 0, nux::MINOR_POSITION_CENTER); + + arrow_layout_ = new nux::VLayout(); + arrow_top_space_ = new nux::SpaceLayout(2, 2, 12, 12); + arrow_bottom_space_ = new nux::SpaceLayout(2, 2, 8, 8); + arrow_layout_->AddView(arrow_top_space_, 0, nux::MINOR_POSITION_CENTER); + arrow_layout_->AddView(expand_icon_, 0, nux::MINOR_POSITION_CENTER); + arrow_layout_->AddView(arrow_bottom_space_, 0, nux::MINOR_POSITION_CENTER); + + filter_layout_->AddView(arrow_layout_, 0, nux::MINOR_POSITION_CENTER); + + layout_->AddView(filter_layout_, 1, nux::MINOR_POSITION_RIGHT, nux::MINOR_SIZE_FULL); + } sig_manager_.Add(new Signal<void, GtkSettings*, GParamSpec*> (gtk_settings_get_default(), "notify::gtk-font-name", @@ -151,9 +194,21 @@ SearchBar::SearchBar(NUX_FILE_LINE_DECL) showing_filters.changed.connect(sigc::mem_fun(this, &SearchBar::OnShowingFiltersChanged)); can_refine_search.changed.connect([&] (bool can_refine) { - show_filters_->SetVisible(can_refine); - expand_icon_->SetVisible(can_refine); + if (show_filter_hint_) + { + show_filters_->SetVisible(can_refine); + expand_icon_->SetVisible(can_refine); + } }); + + disable_glow.changed.connect([&](bool disabled) + { + layout_->SetVerticalExternalMargin(0); + layout_->SetHorizontalExternalMargin(0); + UpdateBackground(true); + QueueDraw(); + }); + } SearchBar::~SearchBar() @@ -259,18 +314,21 @@ gboolean SearchBar::OnSpinnerStartCb(SearchBar* sef) void SearchBar::OnShowingFiltersChanged(bool is_showing) { - dash::Style& style = dash::Style::Instance(); - if (is_showing) - expand_icon_->SetTexture(style.GetGroupUnexpandIcon()); - else - expand_icon_->SetTexture(style.GetGroupExpandIcon()); + if (show_filter_hint_) + { + dash::Style& style = dash::Style::Instance(); + if (is_showing) + expand_icon_->SetTexture(style.GetGroupUnexpandIcon()); + else + expand_icon_->SetTexture(style.GetGroupExpandIcon()); + } } void SearchBar::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) { nux::Geometry geo = GetGeometry(); - UpdateBackground(); + UpdateBackground(false); GfxContext.PushClippingRectangle(geo); @@ -344,21 +402,33 @@ SearchBar::SearchFinished() spinner_->SetState(is_empty ? STATE_READY : STATE_CLEAR); } -void SearchBar::UpdateBackground() +void SearchBar::UpdateBackground(bool force) { -#define PADDING 12 -#define RADIUS 5 + int PADDING = 12; + int RADIUS = 5; int x, y, width, height; nux::Geometry geo = GetGeometry(); geo.width = layered_layout_->GetGeometry().width; - if (geo.width == last_width_ && geo.height == last_height_) + LOG_DEBUG(logger) << "height: " + << geo.height << " - " + << layered_layout_->GetGeometry().height << " - " + << pango_entry_->GetGeometry().height; + + if (geo.width == last_width_ + && geo.height == last_height_ + && force == false) return; last_width_ = geo.width; last_height_ = geo.height; + if (disable_glow) + PADDING = 2; + + x = y = PADDING - 1; + width = last_width_ - (2 * PADDING); height = last_height_ - (2 * PADDING) + 1; @@ -473,5 +543,4 @@ void SearchBar::AddProperties(GVariantBuilder* builder) g_variant_builder_add (builder, "{sv}", "search_string", g_variant_new_string (pango_entry_->GetText().c_str()) ); } -} // namespace dash } // namespace unity diff --git a/plugins/unityshell/src/DashSearchBar.h b/plugins/unityshell/src/SearchBar.h index 7bc8c5e37..bb2d2fe86 100644 --- a/plugins/unityshell/src/DashSearchBar.h +++ b/plugins/unityshell/src/SearchBar.h @@ -17,8 +17,8 @@ * Authored by: Neil Jagdish Patel <neil.patel@canonical.com> */ -#ifndef DASH_SEARCH_BAR_H -#define DASH_SEARCH_BAR_H +#ifndef SEARCH_BAR_H +#define SEARCH_BAR_H #include <gtk/gtk.h> @@ -32,7 +32,7 @@ #include <Nux/TextEntry.h> #include <UnityCore/GLibSignal.h> -#include "DashSearchBarSpinner.h" +#include "SearchBarSpinner.h" #include "IconTexture.h" #include "IMTextEntry.h" #include "Introspectable.h" @@ -40,8 +40,6 @@ namespace unity { -namespace dash -{ using namespace unity::glib; @@ -49,7 +47,10 @@ class SearchBar : public unity::debug::Introspectable, public nux::View { NUX_DECLARE_OBJECT_TYPE(SearchBar, nux::View); public: + typedef nux::ObjectPtr<SearchBar> Ptr; SearchBar(NUX_FILE_LINE_PROTO); + SearchBar(int search_width, bool show_filter_hint, NUX_FILE_LINE_PROTO); + SearchBar(int search_width, NUX_FILE_LINE_PROTO); ~SearchBar(); void SearchFinished(); @@ -59,6 +60,7 @@ public: nux::Property<std::string> search_hint; nux::Property<bool> showing_filters; nux::Property<bool> can_refine_search; + nux::Property<bool> disable_glow; nux::ROProperty<bool> im_active; sigc::signal<void> activated; @@ -67,6 +69,8 @@ public: private: + void Init(); + void OnFontChanged(GtkSettings* settings, GParamSpec* pspec=NULL); void OnSearchHintChanged(); @@ -76,7 +80,7 @@ private: void OnMouseButtonDown(int x, int y, unsigned long button_flags, unsigned long key_flags); void OnEndKeyFocus(); - void UpdateBackground(); + 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(); @@ -85,6 +89,7 @@ private: std::string get_search_string() const; bool set_search_string(std::string const& string); bool get_im_active() const; + bool show_filter_hint_; static gboolean OnLiveSearchTimeout(SearchBar* self); static gboolean OnSpinnerStartCb(SearchBar* self); @@ -110,7 +115,6 @@ private: IconTexture* expand_icon_; int search_bar_width_; - int last_width_; int last_height_; @@ -121,6 +125,5 @@ private: }; } -} #endif diff --git a/plugins/unityshell/src/DashSearchBarSpinner.cpp b/plugins/unityshell/src/SearchBarSpinner.cpp index 3efc7726e..66cdbca63 100644 --- a/plugins/unityshell/src/DashSearchBarSpinner.cpp +++ b/plugins/unityshell/src/SearchBarSpinner.cpp @@ -17,7 +17,7 @@ * Authored by: Neil Jagdish Patel <neil.patel@canonical.com> */ -#include "DashSearchBarSpinner.h" +#include "SearchBarSpinner.h" #include <Nux/VLayout.h> @@ -25,8 +25,6 @@ namespace unity { -namespace dash -{ NUX_IMPLEMENT_OBJECT_TYPE(SearchBarSpinner); @@ -236,4 +234,3 @@ SearchBarSpinner::AcceptKeyNavFocus() } } -} diff --git a/plugins/unityshell/src/DashSearchBarSpinner.h b/plugins/unityshell/src/SearchBarSpinner.h index 097693053..4435208bd 100644 --- a/plugins/unityshell/src/DashSearchBarSpinner.h +++ b/plugins/unityshell/src/SearchBarSpinner.h @@ -17,8 +17,8 @@ * Authored by: Neil Jagdish Patel <neil.patel@canonical.com> */ -#ifndef DASH_SEARCH_BAR_SPINNER_H -#define DASH_SEARCH_BAR_SPINNER_H +#ifndef SEARCH_BAR_SPINNER_H +#define SEARCH_BAR_SPINNER_H #include <Nux/Nux.h> #include <Nux/View.h> @@ -29,8 +29,6 @@ namespace unity { -namespace dash -{ enum SpinnerState { @@ -80,6 +78,5 @@ private: }; } -} #endif diff --git a/plugins/unityshell/src/SwitcherModel.cpp b/plugins/unityshell/src/SwitcherModel.cpp index 9141ea509..37f63991f 100644 --- a/plugins/unityshell/src/SwitcherModel.cpp +++ b/plugins/unityshell/src/SwitcherModel.cpp @@ -40,13 +40,19 @@ SwitcherModel::SwitcherModel(std::vector<AbstractLauncherIcon*> icons) only_detail_on_viewport = false; for (auto icon : _inner) + { + AddChild(icon); icon->Reference(); + } } SwitcherModel::~SwitcherModel() { for (auto icon : _inner) + { + RemoveChild(icon); icon->UnReference(); + } } std::string SwitcherModel::GetName() const @@ -56,22 +62,12 @@ std::string SwitcherModel::GetName() const void SwitcherModel::AddProperties(GVariantBuilder* builder) { - GVariantBuilder children_builder; - g_variant_builder_init(&children_builder, G_VARIANT_TYPE("a(sv)")); - - for (auto icon : _inner) - { - if (icon->GetName() != "") - g_variant_builder_add(&children_builder, "(sv)", icon->GetName().c_str(), icon->Introspect()); - } - unity::variant::BuilderWrapper(builder) .add("detail-selection", detail_selection) .add("detail-selection-index", (int)detail_selection_index) .add("only-detail-on-viewport", only_detail_on_viewport) .add("selection-index", SelectionIndex()) - .add("last-selection-index", LastSelectionIndex()) - .add("children-of-men", g_variant_builder_end(&children_builder)); + .add("last-selection-index", LastSelectionIndex()); } SwitcherModel::iterator diff --git a/plugins/unityshell/src/UBusMessages.h b/plugins/unityshell/src/UBusMessages.h index 69ba90366..1200e3045 100644 --- a/plugins/unityshell/src/UBusMessages.h +++ b/plugins/unityshell/src/UBusMessages.h @@ -78,6 +78,8 @@ // FIXME - fix the nux focus api so we don't need this #define UBUS_RESULT_VIEW_KEYNAV_CHANGED "RESULT_VIEW_KEYNAV_CHANGED" +#define UBUS_HUD_CLOSE_REQUEST "HUD_CLOSE_REQUEST" + // Signals sent when the switcher is shown, hidden or changes selection #define UBUS_SWITCHER_SHOWN "SWITCHER_SHOWN" #define UBUS_SWITCHER_SELECTION_CHANGED "SWITCHER_SELECTION_CHANGED" diff --git a/plugins/unityshell/src/unity-root-accessible.cpp b/plugins/unityshell/src/unity-root-accessible.cpp index c571cc72e..789f83210 100644 --- a/plugins/unityshell/src/unity-root-accessible.cpp +++ b/plugins/unityshell/src/unity-root-accessible.cpp @@ -338,7 +338,7 @@ search_for_launcher_window(UnityRootAccessible* self) nux_object = nux_object_accessible_get_object(accessible); bwindow = dynamic_cast<nux::BaseWindow*>(nux_object); - if ((bwindow!= NULL) && (g_strcmp0(bwindow->GetWindowName().GetTCharPtr(), "Launcher") == 0)) + if ((bwindow!= NULL) && (g_strcmp0(bwindow->GetWindowName().GetTCharPtr(), "LauncherWindow") == 0)) { found = TRUE; break; diff --git a/plugins/unityshell/src/unity-search-bar-accessible.cpp b/plugins/unityshell/src/unity-search-bar-accessible.cpp index 82ad67293..2e3fbabbb 100644 --- a/plugins/unityshell/src/unity-search-bar-accessible.cpp +++ b/plugins/unityshell/src/unity-search-bar-accessible.cpp @@ -20,7 +20,7 @@ * SECTION:unity-search_bar-accessible * @Title: UnitySearchBarAccessible * @short_description: Implementation of the ATK interfaces for #SearchBar - * @see_also: SearchBar at DashSearchBar.h + * @see_also: SearchBar at SearchBar.h * * #UnitySearchBarAccessible implements the required ATK interfaces for * #SearchBar, ie: exposing the different SearchBarIcon on the model as @@ -33,9 +33,9 @@ #include "unity-search-bar-accessible.h" #include "unitya11y.h" -#include "DashSearchBar.h" +#include "SearchBar.h" -using namespace unity::dash; +using namespace unity; /* GObject */ static void unity_search_bar_accessible_class_init(UnitySearchBarAccessibleClass* klass); diff --git a/plugins/unityshell/src/unitya11y.cpp b/plugins/unityshell/src/unitya11y.cpp index c37ae14dc..f5b310b53 100644 --- a/plugins/unityshell/src/unitya11y.cpp +++ b/plugins/unityshell/src/unitya11y.cpp @@ -299,7 +299,7 @@ unity_a11y_create_accessible(nux::Object* object) if (object->Type().IsDerivedFromType(unity::dash::ResultViewGrid::StaticObjectType)) return unity_rvgrid_accessible_new(object); - if (object->Type().IsDerivedFromType(unity::dash::SearchBar::StaticObjectType)) + if (object->Type().IsDerivedFromType(unity::SearchBar::StaticObjectType)) return unity_search_bar_accessible_new(object); if (object->Type().IsDerivedFromType(unity::switcher::SwitcherView::StaticObjectType)) diff --git a/plugins/unityshell/src/unityshell.cpp b/plugins/unityshell/src/unityshell.cpp index 065707bcd..46c792b98 100644 --- a/plugins/unityshell/src/unityshell.cpp +++ b/plugins/unityshell/src/unityshell.cpp @@ -52,7 +52,6 @@ #include "unitya11y.h" -#include "ubus-server.h" #include "UBusMessages.h" #include "UScreen.h" @@ -120,6 +119,7 @@ UnityScreen::UnityScreen(CompScreen* screen) , dash_is_open_ (false) , grab_index_ (0) , painting_tray_ (false) + , last_hud_show_time_(0) { Timer timer; gfloat version; @@ -278,6 +278,8 @@ UnityScreen::UnityScreen(CompScreen* screen) } #endif + optionSetShowHudInitiate(boost::bind(&UnityScreen::ShowHudInitiate, this, _1, _2, _3)); + optionSetShowHudTerminate(boost::bind(&UnityScreen::ShowHudTerminate, this, _1, _2, _3)); optionSetBackgroundColorNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLauncherHideModeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetBacklightModeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); @@ -393,7 +395,7 @@ void UnityScreen::initAltTabNextWindow() sout << "<Alt>" << XKeysymToString(above_tab_keysym); screen->removeAction(&optionGetAltTabNextWindow()); - + CompAction action = CompAction(); action.keyFromString(sout.str()); action.setState (CompAction::StateInitKey | CompAction::StateAutoGrab); @@ -408,7 +410,7 @@ void UnityScreen::initAltTabNextWindow() sout << "<Alt><Shift>" << XKeysymToString(above_tab_keysym); screen->removeAction(&optionGetAltTabPrevWindow()); - + CompAction action = CompAction(); action.keyFromString(sout.str()); action.setState (CompAction::StateInitKey | CompAction::StateAutoGrab); @@ -842,8 +844,8 @@ void UnityScreen::paintDisplay(const CompRegion& region, const GLMatrix& transfo bool UnityScreen::forcePaintOnTop () { return !allowWindowPaint || - ((switcher_controller_->Visible() || - dash_is_open_) && !fullscreen_windows_.empty () && (!(screen->grabbed () && !screen->otherGrabExist (NULL)))); + ((switcher_controller_->Visible() || + dash_is_open_) && !fullscreen_windows_.empty () && (!(screen->grabbed () && !screen->otherGrabExist (NULL)))); } void UnityWindow::paintThumbnail (nux::Geometry const& bounding, float alpha) @@ -989,7 +991,7 @@ bool UnityWindow::handleAnimations (unsigned int ms) { if (mShowdesktopHandler) if (mShowdesktopHandler->animate (ms)) - { + { delete mShowdesktopHandler; mShowdesktopHandler = NULL; return true; @@ -1018,7 +1020,7 @@ bool UnityShowdesktopHandler::shouldHide (CompWindow *w) return false; if (w->state () & (CompWindowStateSkipPagerMask | - CompWindowStateSkipTaskbarMask)) + CompWindowStateSkipTaskbarMask)) return false; if ((w->state () & CompWindowStateHiddenMask)) @@ -1691,7 +1693,7 @@ bool UnityScreen::altTabNextWindowInitiate(CompAction* action, CompAction::State altTabInitiateCommon(action, state, options); switcher_controller_->Select(1); // always select the current application } - + switcher_controller_->NextDetail(); action->setState(action->state() | CompAction::StateTermKey); @@ -1702,7 +1704,7 @@ bool UnityScreen::altTabPrevWindowInitiate(CompAction* action, CompAction::State { if (switcher_controller_->Visible()) switcher_controller_->PrevDetail(); - + return false; } @@ -1757,6 +1759,44 @@ void UnityScreen::OnLauncherEndKeyNav(GVariant* data) PluginAdapter::Default ()->restoreInputFocus (); } +bool UnityScreen::ShowHudInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) +{ + // to receive the Terminate event + if (state & CompAction::StateInitKey) + action->setState(action->state() | CompAction::StateTermKey); + + last_hud_show_time_ = g_get_monotonic_time(); + + return false; +} + +bool UnityScreen::ShowHudTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options) +{ + if (optionGetShowHud().key().toString() == action->key().toString()) + { + if (switcher_controller_->Visible()) + return false; // early exit if the switcher is open + + gint64 current_time = g_get_monotonic_time(); + if (current_time - last_hud_show_time_ < 150 * 1000) + { + if (hud_controller_->IsVisible()) + { + ubus_manager_.SendMessage(UBUS_HUD_CLOSE_REQUEST); + } + else + { + hud_controller_->ShowHud(); + } + last_hud_show_time_ = 0; + } + } + + action->setState(action->state() & ~CompAction::StateTermKey); + + return false; +} + gboolean UnityScreen::initPluginActions(gpointer data) { CompPlugin* p = CompPlugin::find("expo"); @@ -1843,7 +1883,7 @@ bool UnityScreen::initPluginForScreen(CompPlugin* p) bool result = screen->initPluginForScreen(p); if (p->vTable->name() == "unityshell") initAltTabNextWindow(); - + return result; } @@ -1856,8 +1896,8 @@ std::string UnityScreen::GetName() const return "Unity"; } -bool isNuxWindow (CompWindow* value) -{ +bool isNuxWindow (CompWindow* value) +{ std::vector<Window> const& xwns = nux::XInputWindow::NativeHandleList(); auto id = value->id(); @@ -1884,7 +1924,7 @@ const CompWindowList& UnityScreen::getWindowPaintList() void UnityScreen::RaiseInputWindows() { std::vector<Window> const& xwns = nux::XInputWindow::NativeHandleList(); - + for (auto window : xwns) { CompWindow* cwin = screen->findWindow(window); @@ -2017,7 +2057,7 @@ UnityWindow::focus () if (!window->onCurrentDesktop ()) return false; - /* Only withdrawn windows + /* Only withdrawn windows * which are marked hidden * are excluded */ if (!window->shaded () && @@ -2025,8 +2065,8 @@ UnityWindow::focus () (window->state () & CompWindowStateHiddenMask)) return false; - if (window->geometry ().x () + window->geometry ().width () <= 0 || - window->geometry ().y () + window->geometry ().height () <= 0 || + if (window->geometry ().x () + window->geometry ().width () <= 0 || + window->geometry ().y () + window->geometry ().height () <= 0 || window->geometry ().x () >= (int) screen->width ()|| window->geometry ().y () >= (int) screen->height ()) return false; @@ -2132,7 +2172,7 @@ void UnityWindow::stateChangeNotify(unsigned int lastState) !(lastState & CompWindowStateFullscreenMask)) UnityScreen::get (screen)->fullscreen_windows_.push_back(window); else if (lastState & CompWindowStateFullscreenMask && - !(window->state () & CompWindowStateFullscreenMask)) + !(window->state () & CompWindowStateFullscreenMask)) UnityScreen::get (screen)->fullscreen_windows_.remove(window); PluginAdapter::Default()->NotifyStateChange(window, window->state(), lastState); @@ -2281,9 +2321,9 @@ void UnityScreen::optionChanged(CompOption* opt, UnityshellOptions::Options num) { case UnityshellOptions::BackgroundColor: { - nux::Color override_color (optionGetBackgroundColorRed() / 65535.0f, - optionGetBackgroundColorGreen() / 65535.0f, - optionGetBackgroundColorBlue() / 65535.0f, + nux::Color override_color (optionGetBackgroundColorRed() / 65535.0f, + optionGetBackgroundColorGreen() / 65535.0f, + optionGetBackgroundColorBlue() / 65535.0f, optionGetBackgroundColorAlpha() / 65535.0f); override_color.red = override_color.red / override_color.alpha; @@ -2331,6 +2371,7 @@ void UnityScreen::optionChanged(CompOption* opt, UnityshellOptions::Options num) launcher_options->icon_size = optionGetIconSize(); launcher_options->tile_size = optionGetIconSize() + 6; + hud_controller_->launcher_width = launcher_controller_->launcher().GetAbsoluteWidth() - 1; /* The launcher geometry includes 1px used to draw the right margin * that must not be considered when drawing the dash */ dash_controller_->launcher_width = launcher_controller_->launcher().GetAbsoluteWidth() - 1; @@ -2519,6 +2560,11 @@ void UnityScreen::initLauncher() /* Setup Places */ dash_controller_.reset(new dash::Controller()); dash_controller_->on_realize.connect(sigc::mem_fun(this, &UnityScreen::OnDashRealized)); + + /* Setup Hud */ + hud_controller_.reset(new hud::Controller()); + AddChild(hud_controller_.get()); + LOG_INFO(logger) << "initLauncher-hud " << timer.ElapsedSeconds() << "s"; // Setup Shortcut Hint InitHints(); @@ -2668,7 +2714,7 @@ UnityWindow::~UnityWindow() if (mShowdesktopHandler) delete mShowdesktopHandler; - + if (focusdesktop_handle_) g_source_remove(focusdesktop_handle_); diff --git a/plugins/unityshell/src/unityshell.h b/plugins/unityshell/src/unityshell.h index 6066e575b..d8ba88dd8 100644 --- a/plugins/unityshell/src/unityshell.h +++ b/plugins/unityshell/src/unityshell.h @@ -58,6 +58,8 @@ #include <compiztoolbox/compiztoolbox.h> #include <dlfcn.h> +#include "HudController.h" + namespace unity { @@ -202,6 +204,9 @@ public: bool altTabNextWindowInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabPrevWindowInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); + /* handle hud key activations */ + bool ShowHudInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); + bool ShowHudTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool launcherSwitcherForwardInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool launcherSwitcherPrevInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool launcherSwitcherTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); @@ -268,6 +273,7 @@ private: dash::Controller::Ptr dash_controller_; panel::Controller::Ptr panel_controller_; switcher::Controller::Ptr switcher_controller_; + hud::Controller::Ptr hud_controller_; shortcut::Controller::Ptr shortcut_controller_; std::list<shortcut::AbstractHint*> hints_; @@ -322,6 +328,7 @@ private: CompWindowList fullscreen_windows_; bool painting_tray_; unsigned int tray_paint_mask_; + gint64 last_hud_show_time_; #ifndef USE_GLES ScreenEffectFramebufferObject::GLXGetProcAddressProc glXGetProcAddressP; @@ -396,7 +403,7 @@ public: UnityMinimizedHandler *mMinimizeHandler; UnityShowdesktopHandler *mShowdesktopHandler; - + private: guint focusdesktop_handle_; diff --git a/plugins/unityshell/unityshell.xml.in b/plugins/unityshell/unityshell.xml.in index f59218603..4dd37d46b 100644 --- a/plugins/unityshell/unityshell.xml.in +++ b/plugins/unityshell/unityshell.xml.in @@ -40,9 +40,14 @@ </requirement> </deps> <options> - <group> + <group> <_short>Behaviour</_short> - <option name="launcher_hide_mode" type="int"> + <option name="show_hud" type="key"> + <_short>Key to show the HUD</_short> + <_long>Make the HUD appear with this key</_long> + <default><Alt></default> + </option> + <option name="launcher_hide_mode" type="int"> <_short>Hide Launcher</_short> <_long>Make the launcher hide automatically after some time of inactivity: always or just when the focussed window is not over the launcher</_long> <min>0</min> @@ -64,16 +69,16 @@ <value>3</value> <_name>Dodge Active Window</_name> </desc> - </option> - <option name="show_launcher" type="key"> + </option> + <option name="show_launcher" type="key"> <_short>Key to show the launcher</_short> <_long>Make the launcher appear with that key</_long> <default><Super></default> </option> <option name="keyboard_focus" type="key"> - <_short>Key to put keyboard-focus on launcher</_short> - <_long>Set the keyboard-focus on the launcher so it can be navigated with the cursor-keys</_long> - <default><Alt>F1</default> + <_short>Key to put keyboard-focus on launcher</_short> + <_long>Set the keyboard-focus on the launcher so it can be navigated with the cursor-keys</_long> + <default><Alt>F1</default> </option> <option name="execute_command" type="key"> <_short>Key to execute a command</_short> @@ -118,37 +123,37 @@ <_long>fixme</_long> <default><Alt><Shift>Tab</default> </option> - <option name="alt_tab_right" type="key"> - <_short>Go right in the switcher</_short> - <_long>fixme</_long> - <default><Alt>Right</default> - <passive_grab>false</passive_grab> - <internal/> - </option> - <option name="alt_tab_left" type="key"> - <_short>Go left in the switcher</_short> - <_long>fixme</_long> - <default><Alt>Left</default> - <passive_grab>false</passive_grab> - <internal/> - </option> + <option name="alt_tab_right" type="key"> + <_short>Go right in the switcher</_short> + <_long>fixme</_long> + <default><Alt>Right</default> + <passive_grab>false</passive_grab> + <internal/> + </option> + <option name="alt_tab_left" type="key"> + <_short>Go left in the switcher</_short> + <_long>fixme</_long> + <default><Alt>Left</default> + <passive_grab>false</passive_grab> + <internal/> + </option> <option name="alt_tab_detail_start" type="key"> - <_short>Key to expose the windows in the switcher</_short> - <_long>fixme</_long> - <default><Alt>Down</default> - <passive_grab>false</passive_grab> - <internal/> + <_short>Key to expose the windows in the switcher</_short> + <_long>fixme</_long> + <default><Alt>Down</default> + <passive_grab>false</passive_grab> + <internal/> </option> - <option name="alt_tab_detail_stop" type="key"> - <_short>Key to collapse windows in the switcher</_short> - <_long>fixme</_long> - <default><Alt>Up</default> - <passive_grab>false</passive_grab> - <internal/> - </option> + <option name="alt_tab_detail_stop" type="key"> + <_short>Key to collapse windows in the switcher</_short> + <_long>fixme</_long> + <default><Alt>Up</default> + <passive_grab>false</passive_grab> + <internal/> + </option> <option name="alt_tab_next_window" type="key"> - <_short>Key to flip through windows in the switcher</_short> - <_long>fixme</_long> + <_short>Key to flip through windows in the switcher</_short> + <_long>fixme</_long> <passive_grab>false</passive_grab> </option> <option name="alt_tab_prev_window" type="key"> @@ -156,11 +161,11 @@ <_long>fixme</_long> <passive_grab>false</passive_grab> </option> - <option name="show_minimized_windows" type="bool"> - <_short>Show minimized windows in switcher</_short> - <_long>Hack to enable minimized windows in switcher. Disable and report bugs if problems are caused</_long> - <default>true</default> - </option> + <option name="show_minimized_windows" type="bool"> + <_short>Show minimized windows in switcher</_short> + <_long>Hack to enable minimized windows in switcher. Disable and report bugs if problems are caused</_long> + <default>true</default> + </option> </group> <group> <_short>Experimental</_short> @@ -359,7 +364,7 @@ <max>100</max> <default>75</default> </option> - + <option name="devices_option" type="int"> <_short>Show Devices</_short> <_long>Show devices in the launcher</_long> diff --git a/po/POTFILES.in b/po/POTFILES.in index 9154d35f1..18a88b12e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -6,7 +6,7 @@ plugins/unityshell/src/PlacesGroup.cpp plugins/unityshell/src/SpacerLauncherIcon.cpp plugins/unityshell/src/TrashLauncherIcon.cpp plugins/unityshell/src/BFBLauncherIcon.cpp -plugins/unityshell/src/DashSearchBar.cpp +plugins/unityshell/src/SearchBar.cpp plugins/unityshell/src/DashView.cpp plugins/unityshell/src/DesktopLauncherIcon.cpp plugins/unityshell/src/FilterExpanderLabel.cpp diff --git a/standalone-clients/CMakeLists.txt b/standalone-clients/CMakeLists.txt index 8a763eec9..3345dc4e3 100644 --- a/standalone-clients/CMakeLists.txt +++ b/standalone-clients/CMakeLists.txt @@ -8,7 +8,7 @@ set(UNITY_SRC ../plugins/unityshell/src) # Unit tests # find_package (PkgConfig) -set (TEST_DEPS "${UNITY_PLUGIN_DEPS};unity>=4.0.0") +set (TEST_DEPS "${UNITY_PLUGIN_DEPS};unity>=4.0.0 xtst") pkg_check_modules (TEST_UNIT_DEPS REQUIRED ${TEST_DEPS}) set (TESTDATADIR "${CMAKE_CURRENT_SOURCE_DIR}/data") @@ -26,7 +26,7 @@ set (CFLAGS ) add_definitions (${CFLAGS}) -set (LIBS ${TEST_UNIT_DEPS_LIBRARIES} "-lunity-core-${UNITY_API_VERSION} -lm -lGL -lGLU") +set (LIBS ${TEST_UNIT_DEPS_LIBRARIES} "-lunity-core-${UNITY_API_VERSION} -lm -lGL -lGLU -lXtst") link_libraries (${LIBS}) set (LIB_PATHS ${TEST_UNIT_DEPS_LIBRARY_DIRS}) @@ -47,10 +47,10 @@ add_executable (dash ${UNITY_SRC}/BackgroundEffectHelper.h ${UNITY_SRC}/BGHash.cpp ${UNITY_SRC}/BGHash.h - ${UNITY_SRC}/DashSearchBar.cpp - ${UNITY_SRC}/DashSearchBar.h - ${UNITY_SRC}/DashSearchBarSpinner.cpp - ${UNITY_SRC}/DashSearchBarSpinner.h + ${UNITY_SRC}/SearchBar.cpp + ${UNITY_SRC}/SearchBar.h + ${UNITY_SRC}/SearchBarSpinner.cpp + ${UNITY_SRC}/SearchBarSpinner.h ${UNITY_SRC}/DashView.cpp ${UNITY_SRC}/DashView.h ${UNITY_SRC}/DashViewPrivate.cpp @@ -84,6 +84,10 @@ add_executable (dash ${UNITY_SRC}/PlacesSimpleTile.h ${UNITY_SRC}/PlacesVScrollBar.cpp ${UNITY_SRC}/PlacesVScrollBar.h + ${UNITY_SRC}/DashView.cpp + ${UNITY_SRC}/DashView.h + ${UNITY_SRC}/DashViewPrivate.cpp + ${UNITY_SRC}/DashViewPrivate.h ${UNITY_SRC}/DashStyle.cpp ${UNITY_SRC}/IconLoader.cpp ${UNITY_SRC}/IconLoader.h @@ -178,6 +182,8 @@ add_executable (switcher ${UNITY_SRC}/WindowManager.cpp ${UNITY_SRC}/IconRenderer.cpp ${UNITY_SRC}/IconRenderer.h + ${UNITY_SRC}/Introspectable.cpp + ${UNITY_SRC}/Introspectable.h ${UNITY_SRC}/MockLauncherIcon.h ${UNITY_SRC}/BackgroundEffectHelper.h ${UNITY_SRC}/BackgroundEffectHelper.cpp @@ -251,6 +257,8 @@ add_executable (launcher ${UNITY_SRC}/BackgroundEffectHelper.cpp ${UNITY_SRC}/StaticCairoText.cpp ${UNITY_SRC}/StaticCairoText.h + ${UNITY_SRC}/SoftwareCenterLauncherIcon.cpp + ${UNITY_SRC}/SoftwareCenterLauncherIcon.h ${UNITY_SRC}/Introspectable.cpp ${UNITY_SRC}/Introspectable.h ${UNITY_SRC}/QuicklistMenuItem.cpp @@ -286,8 +294,10 @@ add_dependencies (keyutil unity-core-${UNITY_API_VERSION}) add_executable (quicklist ui/TestQuicklist.cpp - ui/EventFaker.cpp - ui/EventFaker.h + nux_test_framework.cpp + nux_test_framework.h + nux_automated_test_framework.cpp + nux_automated_test_framework.h ${UNITY_SRC}/Introspectable.cpp ${UNITY_SRC}/Introspectable.h ${UNITY_SRC}/QuicklistMenuItem.cpp @@ -347,16 +357,18 @@ add_dependencies(filters unity-core-${UNITY_API_VERSION}) add_executable (filter-bar TestFilterBar.cpp + ${UNITY_SRC}/FilterAllButton.cpp + ${UNITY_SRC}/FilterBar.cpp + ${UNITY_SRC}/FilterBasicButton.cpp ${UNITY_SRC}/FilterExpanderLabel.cpp ${UNITY_SRC}/FilterFactory.cpp - ${UNITY_SRC}/FilterBasicButton.cpp - ${UNITY_SRC}/FilterRatingsButton.cpp - ${UNITY_SRC}/FilterRatingsWidget.cpp ${UNITY_SRC}/FilterMultiRangeWidget.cpp ${UNITY_SRC}/FilterMultiRangeButton.cpp ${UNITY_SRC}/FilterGenreButton.cpp ${UNITY_SRC}/FilterGenreWidget.cpp - ${UNITY_SRC}/FilterBar.cpp + ${UNITY_SRC}/FilterRatingsButton.cpp + ${UNITY_SRC}/FilterRatingsWidget.cpp + ${UNITY_SRC}/FilterWidget.cpp ${UNITY_SRC}/DashStyle.cpp ${UNITY_SRC}/JSONParser.cpp ) @@ -456,6 +468,39 @@ add_executable (bg-hash ) add_dependencies (bg-hash unity-core-${UNITY_API_VERSION}) +add_executable (hud + StandaloneHud.cpp + ${UNITY_SRC}/BackgroundEffectHelper.cpp + ${UNITY_SRC}/BackgroundEffectHelper.h + ${UNITY_SRC}/DashSettings.cpp + ${UNITY_SRC}/DashSettings.h + ${UNITY_SRC}/DashStyle.cpp + ${UNITY_SRC}/HudButton.cpp + ${UNITY_SRC}/HudIcon.cpp + ${UNITY_SRC}/HudIcon.h + ${UNITY_SRC}/HudIconTextureSource.cpp + ${UNITY_SRC}/HudIconTextureSource.h + ${UNITY_SRC}/HudView.cpp + ${UNITY_SRC}/IMTextEntry.cpp + ${UNITY_SRC}/Introspectable.cpp + ${UNITY_SRC}/IconTexture.cpp + ${UNITY_SRC}/IconLoader.cpp + ${UNITY_SRC}/IconRenderer.cpp + ${UNITY_SRC}/IconTextureSource.cpp + ${UNITY_SRC}/JSONParser.cpp + ${UNITY_SRC}/OverlayRenderer.cpp + ${UNITY_SRC}/SearchBar.cpp + ${UNITY_SRC}/SearchBarSpinner.cpp + ${UNITY_SRC}/StaticCairoText.cpp + ${UNITY_SRC}/TextureCache.cpp + ${UNITY_SRC}/Timer.cpp + ${UNITY_SRC}/UBusWrapper.cpp + ${UNITY_SRC}/ubus-server.cpp + ${UNITY_SRC}/UScreen.cpp + ${UNITY_SRC}/UScreen.h + ) +add_dependencies (hud unity-core-${UNITY_API_VERSION}) + add_executable (test-shortcut TestShortcut.cpp ${UNITY_SRC}/AbstractSeparator.cpp diff --git a/standalone-clients/StandaloneHud.cpp b/standalone-clients/StandaloneHud.cpp new file mode 100644 index 000000000..03d1aab85 --- /dev/null +++ b/standalone-clients/StandaloneHud.cpp @@ -0,0 +1,171 @@ +/* + * Copyright 2010 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 warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY 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 + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Gordon Allott <gord.allott@canonical.com> + * + */ + +#include <sstream> +#include "Nux/Nux.h" +#include "Nux/VLayout.h" +#include "Nux/Button.h" +#include "Nux/TextureArea.h" +#include "Nux/WindowThread.h" +#include "NuxGraphics/GraphicsEngine.h" +#include <gtk/gtk.h> + +#include "HudView.h" +#include "DashStyle.h" +#include "DashSettings.h" +#include <NuxCore/Logger.h> + +namespace +{ + nux::logging::Logger logger("unity.tests.Hud"); +} + +class TestRunner +{ +public: + TestRunner (); + ~TestRunner (); + + static void InitWindowThread (nux::NThread* thread, void* InitData); + void Init (); + nux::Layout *layout; + unity::hud::View* hud_view_; + unity::dash::Settings dash_settings_; + +private: + unity::hud::Hud hud_service_; +}; + +TestRunner::TestRunner () + : hud_service_("com.canonical.hud", "/com/canonical/hud") +{ +} + +TestRunner::~TestRunner () +{ +} + +void TestRunner::Init () +{ + LOG_WARNING(logger) << "test init"; + layout = new nux::VLayout(); + + hud_view_ = new unity::hud::View(); + + layout->AddView (hud_view_, 1, nux::MINOR_POSITION_TOP, nux::MINOR_SIZE_FULL); + nux::GetWindowCompositor().SetKeyFocusArea(hud_view_->default_focus()); + + nux::GetWindowThread()->SetLayout (layout); + + // things the controller normally does + hud_service_.queries_updated.connect([&] (unity::hud::Hud::Queries queries) { + hud_view_->SetQueries(queries); + std::string icon_name = ""; + for (auto query = queries.begin(); query != queries.end(); query++) + { + if (!(*query)->icon_name.empty()) + { + LOG_DEBUG(logger) << "Setting icon name to: " << (*query)->icon_name; + icon_name = (*query)->icon_name; + break; + } + } + + hud_view_->SetIcon(icon_name); + + }); + + hud_view_->query_activated.connect([&] (unity::hud::Query::Ptr query) { + hud_service_.ExecuteQuery(query, 0); + }); + + hud_view_->query_selected.connect([&] (unity::hud::Query::Ptr query) { + hud_view_->SetIcon(query->icon_name); + }); + + hud_view_->search_changed.connect([&] (std::string search_string) { + hud_service_.RequestQuery(search_string); + }); + + hud_view_->search_activated.connect([&] (std::string search_string) { + hud_service_.ExecuteQueryBySearch(search_string, 0); + }); + + hud_service_.RequestQuery(""); + + hud_view_->SetWindowGeometry(layout->GetAbsoluteGeometry(), layout->GetGeometry()); + +} + +void TestRunner::InitWindowThread(nux::NThread* thread, void* InitData) +{ + TestRunner *self = (TestRunner *) InitData; + self->Init (); +} + +void +ControlThread (nux::NThread* thread, + void* data) +{ + // sleep for 3 seconds + nux::SleepForMilliseconds (3000); + printf ("ControlThread successfully started\n"); +} + + +int main(int argc, char **argv) +{ + nux::SystemThread* st = NULL; + nux::WindowThread* wt = NULL; + + // no real tests right now, just make sure we don't get any criticals and such + // waiting on nice perceptual diff support before we can build real tests + // for views + + g_type_init (); + gtk_init (&argc, &argv); + + nux::NuxInitialize(0); + + // Slightly higher as we're more likely to test things we know will fail + nux::logging::configure_logging("unity.hud=debug"); + + nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); + LOG_DEBUG(logger) << "starting the standalone hud"; + // The instances for the pseudo-singletons. + unity::dash::Style dash_style; + + TestRunner *test_runner = new TestRunner (); + wt = nux::CreateGUIThread(TEXT("Hud Prototype Test"), + 1200, 768, + 0, + &TestRunner::InitWindowThread, + test_runner); + + st = nux::CreateSystemThread (NULL, ControlThread, wt); + + if (st) + st->Start (NULL); + + wt->Run (NULL); + delete st; + delete wt; + return 0; +} diff --git a/standalone-clients/TestFilterBar.cpp b/standalone-clients/TestFilterBar.cpp index 020a4ffb7..e12e2dd06 100644 --- a/standalone-clients/TestFilterBar.cpp +++ b/standalone-clients/TestFilterBar.cpp @@ -53,7 +53,7 @@ TestRunner::~TestRunner () void TestRunner::Init () { - unity::FilterBar *filterbar = new unity::FilterBar(NUX_TRACKER_LOCATION); + auto *filterbar = new unity::dash::FilterBar(NUX_TRACKER_LOCATION); layout = new nux::VLayout(NUX_TRACKER_LOCATION); diff --git a/standalone-clients/TestFilters.cpp b/standalone-clients/TestFilters.cpp index 1e4689b13..cddf2ac0d 100644 --- a/standalone-clients/TestFilters.cpp +++ b/standalone-clients/TestFilters.cpp @@ -57,13 +57,13 @@ TestRunner::~TestRunner () void TestRunner::Init () { - unity::FilterBasicButton *button = new unity::FilterBasicButton ("hello world", NUX_TRACKER_LOCATION); - unity::FilterRatingsWidget *ratings = new unity::FilterRatingsWidget (NUX_TRACKER_LOCATION); - unity::FilterGenreButton *genre_button = new unity::FilterGenreButton ("genre button", NUX_TRACKER_LOCATION); + auto *button = new unity::dash::FilterBasicButton ("hello world", NUX_TRACKER_LOCATION); + auto *ratings = new unity::dash::FilterRatingsWidget (NUX_TRACKER_LOCATION); + auto *genre_button = new unity::dash::FilterGenreButton ("genre button", NUX_TRACKER_LOCATION); - unity::FilterGenre *genre = new unity::FilterGenre(NUX_TRACKER_LOCATION); + auto *genre = new unity::dash::FilterGenre(3, NUX_TRACKER_LOCATION); - unity::FilterMultiRange *multi_range = new unity::FilterMultiRange (NUX_TRACKER_LOCATION); + auto *multi_range = new unity::dash::FilterMultiRange (NUX_TRACKER_LOCATION); layout = new nux::VLayout(NUX_TRACKER_LOCATION); diff --git a/standalone-clients/nux_automated_test_framework.cpp b/standalone-clients/nux_automated_test_framework.cpp new file mode 100644 index 000000000..970db58ea --- /dev/null +++ b/standalone-clients/nux_automated_test_framework.cpp @@ -0,0 +1,461 @@ +/* + * Copyright 2010 Inalogic Inc. + * + * 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 warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY 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 + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Jay Taoko <jaytaoko@inalogic.com> + * + */ + +#include "Nux/Nux.h" +#include <X11/extensions/XTest.h> +#include <X11/keysym.h> +#include "nux_automated_test_framework.h" + + +int NuxAutomatedTestFramework::mouse_motion_time_span = 1000; // milliseconds +int NuxAutomatedTestFramework::mouse_click_time_span = 300; // milliseconds +int NuxAutomatedTestFramework::minimum_sleep_time = 600; // milliseconds +int NuxAutomatedTestFramework::safety_border_inside_view = 1; // pixels + +NuxAutomatedTestFramework::NuxAutomatedTestFramework(nux::WindowThread *window_thread) +{ + ready_to_start_ = false; + display_ = NULL; + window_thread_ = window_thread; + window_x_ = 0; + window_y_ = 0; + window_width_ = 0; + window_height_ = 0; + terminate_when_test_over_ = false; +} + +NuxAutomatedTestFramework::~NuxAutomatedTestFramework() +{ + XCloseDisplay(display_); +} + +void NuxAutomatedTestFramework::SetTerminateProgramWhenDone(bool terminate) +{ + terminate_when_test_over_ = terminate; +} + +bool NuxAutomatedTestFramework::WhenDoneTerminateProgram() +{ + return terminate_when_test_over_; +} + +void NuxAutomatedTestFramework::Startup() +{ + display_ = XOpenDisplay(NULL); + nux::Geometry rect = window_thread_->GetGraphicsDisplay().GetWindowGeometry(); + //nuxDebugMsg("Window geometry: (%d, %d, %d, %d)", rect.x, rect.y, rect.width, rect.height); + + window_x_ = rect.x; + window_y_ = rect.y; + window_width_ = rect.width; + window_height_ = rect.height; +} + +void NuxAutomatedTestFramework::ViewSendMouseClick(nux::View *view, int button) +{ + nux::Rect r; + if (view) + { + r = view->GetAbsoluteGeometry(); + r.OffsetPosition(window_x_ + r.width/2, window_y_ + r.height/2); + } + else + { + r = window_thread_->GetGraphicsDisplay().GetWindowGeometry(); + r.OffsetPosition(r.width/2, r.height/2); + } + + SendFakeMouseMotionEvent(r.x, r.y, NuxAutomatedTestFramework::mouse_motion_time_span); + SendFakeMouseEvent(button, true); + nux::SleepForMilliseconds(NuxAutomatedTestFramework::mouse_click_time_span); + SendFakeMouseEvent(button, false); + + XSync(display_, False); + nux::SleepForMilliseconds(NuxAutomatedTestFramework::minimum_sleep_time); +} + +void NuxAutomatedTestFramework::ViewSendMouseDoubleClick(nux::View *view, int button) +{ + nux::Rect r; + if (view) + { + r = view->GetAbsoluteGeometry(); + r.OffsetPosition(window_x_ + r.width/2, window_y_ + r.height/2); + } + else + { + r = window_thread_->GetGraphicsDisplay().GetWindowGeometry(); + r.OffsetPosition(r.width/2, r.height/2); + } + + // Send the mouse to the center of the view + SendFakeMouseMotionEvent(r.x, r.y, NuxAutomatedTestFramework::mouse_motion_time_span); + + XTestFakeButtonEvent(display_, button, true, CurrentTime); + XTestFakeButtonEvent(display_, button, false, CurrentTime); + XTestFakeButtonEvent(display_, button, true, CurrentTime); + XTestFakeButtonEvent(display_, button, false, CurrentTime); + XSync(display_, False); + nux::SleepForMilliseconds(NuxAutomatedTestFramework::minimum_sleep_time); +} + +void NuxAutomatedTestFramework::ViewSendMouseDown(nux::View *view, int button) +{ + XEvent event; + /* Get the current pointer position */ + XQueryPointer(display_, RootWindow(display_, 0), + &event.xbutton.root, &event.xbutton.window, + &event.xbutton.x_root, &event.xbutton.y_root, + &event.xbutton.x, &event.xbutton.y, + &event.xbutton.state); + + int current_x = event.xbutton.x - window_x_; + int current_y = event.xbutton.y - window_y_; + + nux::Rect r = view->GetAbsoluteGeometry(); + + if (!r.IsInside(nux::Point(current_x, current_y))) + { + // The mouse pointer is not inside the view. + // Move the mouse pointer to the center of the view. + r.OffsetPosition(window_x_, window_y_); + + // Go to the center of the view + int view_center_x = r.x + r.width/2; + int view_center_y = r.y + r.height/2; + SendFakeMouseMotionEvent(view_center_x, view_center_y, NuxAutomatedTestFramework::mouse_motion_time_span); + nux::SleepForMilliseconds(minimum_sleep_time); + } + SendFakeMouseEvent(button, true); +} + +void NuxAutomatedTestFramework::ViewSendMouseUp(nux::View *view, int button) +{ + // nux::Rect r = view->GetAbsoluteGeometry(); + // r.OffsetPosition(window_x_, window_y_); + + // int view_center_x = r.x + r.width/2; + // int view_center_y = r.y + r.height/2; + + // SendFakeMouseMotionEvent(view_center_x, view_center_y, 1000); + // nux::SleepForMilliseconds(minimum_sleep_time); + SendFakeMouseEvent(button, false); +} + +void NuxAutomatedTestFramework::ViewSendMouseDrag(nux::View *view, int button_index, int x0, int y0, int x1, int y1) +{ + nux::Rect r0 = view->GetAbsoluteGeometry(); + nux::Rect r1 = view->GetAbsoluteGeometry(); + r0.OffsetPosition(window_x_ + x0, window_y_ + y0); + r1.OffsetPosition(window_x_ + x1, window_y_ + y1); + + // Go to first point + SendFakeMouseMotionEvent(r0.x, r0.y, NuxAutomatedTestFramework::mouse_motion_time_span); + nux::SleepForMilliseconds(minimum_sleep_time); + + // Mouse down + ViewSendMouseDown(view, button_index); + + // Drag to second point + SendFakeMouseMotionEvent(r1.x, r1.y, NuxAutomatedTestFramework::mouse_motion_time_span); + nux::SleepForMilliseconds(minimum_sleep_time); + + // Mouse up + ViewSendMouseUp(view, button_index); +} + +void NuxAutomatedTestFramework::ViewSendMouseMotionTo(nux::View *view, int x, int y) +{ + nux::Rect r; + if (view) + { + r = view->GetAbsoluteGeometry(); + r.OffsetPosition(window_x_ + x, window_y_ + y); + } + else + { + r = window_thread_->GetGraphicsDisplay().GetWindowGeometry(); + r.OffsetPosition(x, y); + } + + SendFakeMouseMotionEvent(r.x, r.y, NuxAutomatedTestFramework::mouse_motion_time_span); +} + +void NuxAutomatedTestFramework::ViewSendMouseMotionToCenter(nux::View *view) +{ + nux::Rect r; + if (view) + { + r = view->GetAbsoluteGeometry(); + r.OffsetPosition(window_x_, window_y_); + } + else + { + r = window_thread_->GetGraphicsDisplay().GetWindowGeometry(); + } + + int view_center_x = r.x + r.width/2; + int view_center_y = r.y + r.height/2; + + SendFakeMouseMotionEvent(view_center_x, view_center_y, NuxAutomatedTestFramework::mouse_motion_time_span); +} + +void NuxAutomatedTestFramework::ViewSendMouseMotionToTopLeft(nux::View *view) +{ + nux::Rect r = view->GetAbsoluteGeometry(); + r.OffsetPosition(window_x_, window_y_); + + SendFakeMouseMotionEvent(r.x + safety_border_inside_view, r.y + safety_border_inside_view, NuxAutomatedTestFramework::mouse_motion_time_span); +} + +void NuxAutomatedTestFramework::ViewSendMouseMotionToTopRight(nux::View *view) +{ + nux::Rect r = view->GetAbsoluteGeometry(); + r.OffsetPosition(window_x_, window_y_); + + SendFakeMouseMotionEvent(r.x + r.width-1, r.y+safety_border_inside_view, NuxAutomatedTestFramework::mouse_motion_time_span); +} + +void NuxAutomatedTestFramework::ViewSendMouseMotionToBottomLeft(nux::View *view) +{ + nux::Rect r = view->GetAbsoluteGeometry(); + r.OffsetPosition(window_x_, window_y_); + + SendFakeMouseMotionEvent(r.x+safety_border_inside_view, r.y + r.height-1, NuxAutomatedTestFramework::mouse_motion_time_span); +} + +void NuxAutomatedTestFramework::ViewSendMouseMotionToBottomRight(nux::View *view) +{ + nux::Rect r = view->GetAbsoluteGeometry(); + r.OffsetPosition(window_x_, window_y_); + + SendFakeMouseMotionEvent(r.x + r.width-1, r.y + r.height-1, NuxAutomatedTestFramework::mouse_motion_time_span); +} + +void NuxAutomatedTestFramework::ViewSendChar(const char c) +{ + KeySym modifier = 0; + + if ((c >= 'A') && (c <= 'Z')) + { + modifier = XK_Shift_L; + } + + std::string s(1, c); + SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier); + nux::SleepForMilliseconds(300); +} + +void NuxAutomatedTestFramework::ViewSendString(const std::string &str) +{ + int l = str.length(); + if (l == 0) + return; + + int i = 0; + + while (i < l) + { + KeySym modifier = 0; + char c = str[i++]; + + if ((c >= 'A') && (c <= 'Z')) + { + modifier = XK_Shift_L; + } + + std::string s(1, c); + SendFakeKeyEvent(XStringToKeysym(s.c_str()), modifier); + nux::SleepForMilliseconds(300); + } +} + +void NuxAutomatedTestFramework::ViewSendKeyCombo(KeySym modsym0, KeySym modsym1, KeySym modsym2, const char c) +{ + KeyCode keycode = 0; + KeyCode modcode0 = 0; + KeyCode modcode1 = 0; + KeyCode modcode2 = 0; + + std::string s(1, c); + keycode = XKeysymToKeycode(display_, XStringToKeysym(s.c_str())); + XTestGrabControl(display_, True); + + /* Generate modkey press */ + if (modsym0 != 0) + { + modcode0 = XKeysymToKeycode(display_, modsym0); + XTestFakeKeyEvent(display_, modcode0, True, 0); + } + if (modsym1 != 0) + { + modcode1 = XKeysymToKeycode(display_, modsym1); + XTestFakeKeyEvent(display_, modcode1, True, 0); + } + if (modsym2 != 0) + { + modcode2 = XKeysymToKeycode(display_, modsym2); + XTestFakeKeyEvent(display_, modcode2, True, 0); + } + + /* Generate regular key press and release */ + XTestFakeKeyEvent(display_, keycode, True, 0); + XTestFakeKeyEvent(display_, keycode, False, 0); + + /* Generate modkey release */ + if (modsym0 != 0) + { + XTestFakeKeyEvent(display_, modcode0, False, 0); + } + if (modsym1 != 0) + { + XTestFakeKeyEvent(display_, modcode1, False, 0); + } + if (modsym2 != 0) + { + XTestFakeKeyEvent(display_, modcode2, False, 0); + } + + XSync(display_, False); + XTestGrabControl(display_, False); +} + +void NuxAutomatedTestFramework::ViewSendCtrlA() +{ + ViewSendKeyCombo(XK_Control_L, 0, 0, 'a'); +} + +void NuxAutomatedTestFramework::ViewSendDelete() +{ + SendFakeKeyEvent(XK_Delete, 0); +} + +void NuxAutomatedTestFramework::ViewSendBackspace() +{ + SendFakeKeyEvent(XK_BackSpace, 0); +} + +void NuxAutomatedTestFramework::ViewSendEscape() +{ + SendFakeKeyEvent(XK_Escape, 0); +} + +void NuxAutomatedTestFramework::ViewSendTab() +{ + SendFakeKeyEvent(XK_Tab, 0); +} + +void NuxAutomatedTestFramework::ViewSendReturn() +{ + SendFakeKeyEvent(XK_Return, 0); +} + +void NuxAutomatedTestFramework::PutMouseAt(int x, int y) +{ + XTestFakeMotionEvent(display_, XScreenNumberOfScreen(DefaultScreenOfDisplay(display_)), x, y, CurrentTime); + XSync(display_, False); +} + +void NuxAutomatedTestFramework::SendFakeKeyEvent(KeySym keysym, KeySym modsym) +{ + KeyCode keycode = 0; + KeyCode modcode = 0; + + keycode = XKeysymToKeycode(display_, keysym); + XTestGrabControl(display_, True); + + /* Generate modkey press */ + if (modsym != 0) + { + modcode = XKeysymToKeycode(display_, modsym); + XTestFakeKeyEvent(display_, modcode, True, 0); + } + + /* Generate regular key press and release */ + XTestFakeKeyEvent(display_, keycode, True, 0); + XTestFakeKeyEvent(display_, keycode, False, 0); + + /* Generate modkey release */ + if (modsym != 0) + { + XTestFakeKeyEvent(display_, modcode, False, 0); + } + + XSync(display_, False); + XTestGrabControl(display_, False); +} + +void NuxAutomatedTestFramework::SendFakeMouseEvent(int mouse_button_index, bool pressed) +{ + XTestFakeButtonEvent(display_, mouse_button_index, pressed, CurrentTime); + XSync(display_, False); +} + +void NuxAutomatedTestFramework::SendFakeMouseMotionEvent(int x, int y, int ms_delay) +{ + XEvent event; + /* Get the current pointer position */ + XQueryPointer(display_, RootWindow(display_, 0), + &event.xbutton.root, &event.xbutton.window, + &event.xbutton.x_root, &event.xbutton.y_root, + &event.xbutton.x, &event.xbutton.y, + &event.xbutton.state); + + int old_x = event.xbutton.x; + int old_y = event.xbutton.y; + + int n_iteration = ms_delay / 16.0f; + + //nuxDebugMsg("n_iteration: %d", n_iteration); + + if (n_iteration < 1) + { + n_iteration = 1; + } + + XSync(display_, False); + + for (int i = 0; i < n_iteration; i++) + { + float t = ((float)i + 1.0f) / n_iteration; + + int cx = (1.0f - t) * old_x + t * x; + int cy = (1.0f - t) * old_y + t * y; + XTestFakeMotionEvent(display_, XScreenNumberOfScreen(DefaultScreenOfDisplay(display_)), cx, cy, CurrentTime); + XSync(display_, False); + usleep(16*1000); + } + + XTestFakeMotionEvent(display_, XScreenNumberOfScreen(DefaultScreenOfDisplay(display_)), x, y, CurrentTime); + XSync(display_, False); + nux::SleepForMilliseconds(NuxAutomatedTestFramework::minimum_sleep_time); +} + +void NuxAutomatedTestFramework::TestReportMsg(bool b, const char* msg) +{ + if (b) + { + nuxOkMsg("%s: %s", msg, "Ok"); + } + else + { + nuxAlertMsg("%s: %s", msg, "Failed"); + } +} \ No newline at end of file diff --git a/standalone-clients/nux_automated_test_framework.h b/standalone-clients/nux_automated_test_framework.h new file mode 100644 index 000000000..63c9b52d7 --- /dev/null +++ b/standalone-clients/nux_automated_test_framework.h @@ -0,0 +1,127 @@ +/* + * Copyright 2010 Inalogic Inc. + * + * 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 warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY 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 + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Jay Taoko <jaytaoko@inalogic.com> + * + */ + +#include <X11/extensions/XTest.h> +#include <X11/keysym.h> + +#ifndef NUX_AUTOMATED_TEST_FRAMEWORK_H +#define NUX_AUTOMATED_TEST_FRAMEWORK_H + +class NuxAutomatedTestFramework +{ +public: + NuxAutomatedTestFramework(nux::WindowThread *window_thread); + virtual ~NuxAutomatedTestFramework(); + + //! Initialize the testing framework. + void Startup(); + + //! Simulate a mouse click event on a view. + /*! + Move the mouse to the middle of the view (if it isn't there already) and perform a click event. + */ + void ViewSendMouseClick(nux::View *view, int button); + //! Simulate a mouse double click event on a view. + /*! + Move the mouse to the middle of the view (if it isn't there already) and perform a double click event. + */ + void ViewSendMouseDoubleClick(nux::View *view, int button); + //! Simulate a mouse down event on a view. + void ViewSendMouseDown(nux::View *view, int button); + //! Simulate a mouse up event on a view. + void ViewSendMouseUp(nux::View *view, int button); + //! Simulate a drag event on a view from (x0, y0) to (x1, y1). + void ViewSendMouseDrag(nux::View *view, int button, int x0, int y0, int x1, int y1); + //! Simulate mouse motion to (x, y). + void ViewSendMouseMotionTo(nux::View *view, int x, int y); + //! Simulate mouse motion to the center of a view. + void ViewSendMouseMotionToCenter(nux::View *view); + //! Simulate mouse motion to the top left corner of a view. + void ViewSendMouseMotionToTopLeft(nux::View *view); + //! Simulate mouse motion to the top right corner of a view. + void ViewSendMouseMotionToTopRight(nux::View *view); + //! Simulate mouse motion to the bottom left corner of a view. + void ViewSendMouseMotionToBottomLeft(nux::View *view); + //! Simulate mouse motion to the bottom right corner of a view. + void ViewSendMouseMotionToBottomRight(nux::View *view); + + //! Simulate a key event. + void ViewSendChar(const char c); + //! Simulate a succession of key events. + void ViewSendString(const std::string &str); + //! Simulate a key combo. + void ViewSendKeyCombo(KeySym modsym0, KeySym modsym1, KeySym modsym2, const char c); + //! Simulate Ctrl+a. + void ViewSendCtrlA(); + //! Simulate Delete key. + void ViewSendDelete(); + //! Simulate Backspace key. + void ViewSendBackspace(); + //! Simulate Escape key. + void ViewSendEscape(); + //! Simulate Tab key. + void ViewSendTab(); + //! Simulate Return key. + void ViewSendReturn(); + + //! Put the mouse pointer anywhere on the display. + void PutMouseAt(int x, int y); + + //! Simulate a mouse event. + void SendFakeMouseEvent(int mouse_button_index, bool pressed); + //! Simulate a key event. + void SendFakeKeyEvent(KeySym keysym, KeySym modsym); + //! Simulate a mouse motion event. + void SendFakeMouseMotionEvent(int x, int y, int ms_delay); + + /*! + Set the test thread to terminae the program when testing is over. + */ + void SetTerminateProgramWhenDone(bool terminate); + /*! + Return true if the test thread is allowed to terminate the program after testing is over. + */ + bool WhenDoneTerminateProgram(); + + /*! + Print a report message to the console. + */ + void TestReportMsg(bool b, const char* msg); + +private: + void WindowConfigSignal(int x, int y, int width, int height); + + bool ready_to_start_; + Display* display_; + nux::WindowThread *window_thread_; + int window_x_; + int window_y_; + int window_width_; + int window_height_; + bool terminate_when_test_over_; + + static int mouse_motion_time_span; // in milliseconds + static int mouse_click_time_span; // in milliseconds + static int minimum_sleep_time; // in milliseconds + static int safety_border_inside_view; // in pixels +}; + +#endif // NUX_AUTOMATED_TEST_FRAMEWORK_H + diff --git a/standalone-clients/nux_test_framework.cpp b/standalone-clients/nux_test_framework.cpp new file mode 100644 index 000000000..f839e169e --- /dev/null +++ b/standalone-clients/nux_test_framework.cpp @@ -0,0 +1,129 @@ +/* + * Copyright 2010 Inalogic Inc. + * + * 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 warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY 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 + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Jay Taoko <jaytaoko@inalogic.com> + * + */ + +#include "Nux/Nux.h" +#include "Nux/VLayout.h" +#include "Nux/HLayout.h" +#include "Nux/WindowThread.h" +#include "Nux/TextEntry.h" +#include "nux_test_framework.h" + + +NuxTestFramework::NuxTestFramework(const char* program_name, + int window_width, + int window_height, + int program_life_span) +{ + ready_to_go_ = false; + window_width_ = window_width; + window_height_ = window_height; + + if (window_width_ < 100) + window_width_ = 100; + + if (window_height_ < 100) + window_height_ = 100; + + timeout_signal_ = NULL; + window_thread_ = NULL; + program_name_ = program_name; + program_life_span_ = program_life_span; + + if (program_life_span_ > 0 && program_life_span_ < 1000) + { + // Minimum life span is 1 second. + program_life_span_ = 1000; + } +} + +NuxTestFramework::~NuxTestFramework() +{ + if (window_thread_) + delete window_thread_; +} + +void NuxTestFramework::Startup() +{ + nux::NuxInitialize(0); + window_thread_ = nux::CreateGUIThread(program_name_.c_str(), window_width_, window_height_, NULL, NULL, NULL); + + window_thread_->window_configuration.connect(sigc::mem_fun(this, &NuxTestFramework::WaitForConfigureEvent)); +} + +void NuxTestFramework::UserInterfaceSetup() +{ + // nux::VLayout *MainVLayout = new nux::VLayout(NUX_TRACKER_LOCATION); + // nux::TextEntry *text_entry_0 = new nux::TextEntry(TEXT("0123456789abcdefghij"), NUX_TRACKER_LOCATION); + + // MainVLayout->AddView(text_entry_0, 0, nux::eCenter, nux::eFull); + // MainVLayout->SetVerticalInternalMargin(10); + // MainVLayout->SetContentDistribution(nux::eStackCenter); + + // nux::GetWindowThread()->SetLayout(MainVLayout); + // nux::ColorLayer background(nux::Color(0xFF4D4D4D)); + // window_thread_->SetWindowBackgroundPaintLayer(&background); +} + +void NuxTestFramework::Run() +{ + if (window_thread_ == NULL) + return; + + if (program_life_span_ > 0) + { + timeout_signal_ = new nux::TimeOutSignal(); + timeout_signal_->time_expires.connect(sigc::mem_fun(this, &NuxTestFramework::ProgramExitCall)); + window_thread_->GetTimerHandler().AddTimerHandler(program_life_span_, timeout_signal_, NULL, NULL); + } + + window_thread_->Run(NULL); +} + +bool NuxTestFramework::ReadyToGo() +{ + return window_thread_; +} + +nux::WindowThread* NuxTestFramework::GetWindowThread() +{ + return window_thread_; +} + +void NuxTestFramework::ProgramExitCall(void *data) +{ + if (window_thread_) + window_thread_->ExitMainLoop(); +} + +void NuxTestFramework::WaitForConfigureEvent(int x, int y, int width, int height) +{ + ready_to_go_ = true; +} + + +// int main(int argc, char **argv) +// { +// NuxTestFramework test("Text Entry", 300, 300, 3000); +// test.Startup(); +// test.UserInterfaceSetup(); +// test.Run(); + +// return 0; +// } diff --git a/standalone-clients/nux_test_framework.h b/standalone-clients/nux_test_framework.h new file mode 100644 index 000000000..59855c986 --- /dev/null +++ b/standalone-clients/nux_test_framework.h @@ -0,0 +1,61 @@ +/* + * Copyright 2010 Inalogic Inc. + * + * 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 warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY 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 + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Jay Taoko <jaytaoko@inalogic.com> + * + */ + +#include "Nux/Nux.h" +#include "Nux/VLayout.h" +#include "Nux/HLayout.h" +#include "Nux/WindowThread.h" +#include "Nux/TextEntry.h" + +#ifndef NUXTESTFRAMEWORK_H +#define NUXTESTFRAMEWORK_H + +class NuxTestFramework +{ +public: + NuxTestFramework(const char* program_name, int window_width, int window_height, int program_life_span); + virtual ~NuxTestFramework(); + + virtual void Startup(); + virtual void UserInterfaceSetup(); + virtual void Run(); + + bool ReadyToGo(); + + nux::WindowThread* GetWindowThread(); + +public: + std::string program_name_; + int program_life_span_; //!< The program will auto-terminate after a delay in milliseconds. + nux::TimeOutSignal *timeout_signal_; + + nux::WindowThread *window_thread_; + + int window_width_; + int window_height_; + +private: + void ProgramExitCall(void *data); + void WaitForConfigureEvent(int x, int y, int width, int height); + bool ready_to_go_; +}; + +#endif // NUXTESTFRAMEWORK_H + diff --git a/standalone-clients/ui/TestQuicklist.cpp b/standalone-clients/ui/TestQuicklist.cpp index 7b484e5af..75ac309fc 100644 --- a/standalone-clients/ui/TestQuicklist.cpp +++ b/standalone-clients/ui/TestQuicklist.cpp @@ -14,11 +14,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Authored by: Mirco Müller <mirco.mueller@canonical.com> + * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include <glib.h> #include <gtk/gtk.h> -#include <dbus/dbus-glib.h> #include "Nux/Nux.h" #include "Nux/VLayout.h" @@ -31,101 +31,175 @@ #include "QuicklistMenuItemCheckmark.h" #include "QuicklistMenuItemRadio.h" -#include "EventFaker.h" -#include <X11/Xlib.h> +#include "nux_test_framework.h" +#include "nux_automated_test_framework.h" #define WIN_WIDTH 400 -#define WIN_HEIGHT 300 +#define WIN_HEIGHT 400 -gboolean gResult[3] = {false, false, false}; +class TestQuicklist: public NuxTestFramework +{ +public: + TestQuicklist(const char *program_name, int window_width, int window_height, int program_life_span); + ~TestQuicklist(); + + virtual void UserInterfaceSetup(); + int ItemNaturalPosition(QuicklistMenuItem* item); + bool HasNthItemActivated(unsigned int index); + + QuicklistView* quicklist_; + std::map<QuicklistMenuItem*,bool> activated_; + +private: + QuicklistMenuItemSeparator* createSeparatorItem(); + QuicklistMenuItemLabel* createLabelItem(std::string const& label, bool enabled = true); + QuicklistMenuItemCheckmark* createCheckmarkItem(std::string const& label, bool enabled, bool checked); + QuicklistMenuItemRadio* createRadioItem(std::string const& label, bool enabled, bool checked); + void AddItem(QuicklistMenuItem* item); + + void connectToActivatedSignal(DbusmenuMenuitem* item); + static void activatedCallback(DbusmenuMenuitem* item, int time, gpointer data); + + std::map<DbusmenuMenuitem*, QuicklistMenuItem*> menus2qitem_; +}; + +TestQuicklist::TestQuicklist(const char *program_name, int window_width, int window_height, int program_life_span) + : NuxTestFramework(program_name, window_width, window_height, program_life_span), + quicklist_(nullptr) +{} + +TestQuicklist::~TestQuicklist() +{ + if (quicklist_) + quicklist_->UnReference(); +} + +int TestQuicklist::ItemNaturalPosition(QuicklistMenuItem* item) +{ + int pos = 1; + + for (auto it : quicklist_->GetChildren()) + { + if (it == item) + return pos; + + if (it->GetItemType() != MENUITEM_TYPE_SEPARATOR) + pos++; + } -QuicklistView* gQuicklist = NULL; -QuicklistMenuItemCheckmark* gCheckmark = NULL; -QuicklistMenuItemRadio* gRadio = NULL; -QuicklistMenuItemLabel* gLabel = NULL; + return -1; +} -void -activatedCallback (DbusmenuMenuitem* item, - int time, - gpointer data) +bool TestQuicklist::HasNthItemActivated(unsigned int index) { - gboolean* result = (gboolean*) data; + return activated_[quicklist_->GetNthItems(index)]; +} - *result = true; +void TestQuicklist::activatedCallback(DbusmenuMenuitem* item, int time, gpointer data) +{ + auto self = static_cast<TestQuicklist*>(data); + QuicklistMenuItem* qitem = self->menus2qitem_[item]; + + if (!self->activated_[qitem]) + { + self->activated_[qitem] = true; + g_debug("Quicklist-item %d activated", self->ItemNaturalPosition(qitem)); + } +} - g_print ("Quicklist-item activated\n"); +void TestQuicklist::connectToActivatedSignal(DbusmenuMenuitem* item) +{ + g_signal_connect (item, + DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, + G_CALLBACK (&TestQuicklist::activatedCallback), + this); } -QuicklistMenuItemCheckmark* -createCheckmarkItem () +QuicklistMenuItemSeparator* TestQuicklist::createSeparatorItem() { DbusmenuMenuitem* item = NULL; - QuicklistMenuItemCheckmark* checkmark = NULL; + QuicklistMenuItemSeparator* separator = NULL; + + item = dbusmenu_menuitem_new (); + + dbusmenu_menuitem_property_set_bool (item, + DBUSMENU_MENUITEM_PROP_ENABLED, + true); + + separator = new QuicklistMenuItemSeparator (item, true); + menus2qitem_[item] = separator; + + return separator; +} + +QuicklistMenuItemRadio* TestQuicklist::createRadioItem(std::string const& label, bool enabled, bool checked) +{ + DbusmenuMenuitem* item = NULL; + QuicklistMenuItemRadio* radio = NULL; item = dbusmenu_menuitem_new (); dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_LABEL, - "Unchecked"); + label.c_str()); dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, - DBUSMENU_MENUITEM_TOGGLE_CHECK); + DBUSMENU_MENUITEM_TOGGLE_RADIO); dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, - true); + enabled); dbusmenu_menuitem_property_set_int (item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, - DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED); + (checked ? + DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : + DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED + )); - checkmark = new QuicklistMenuItemCheckmark (item, true); - - g_signal_connect (item, - DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (activatedCallback), - &gResult[0]); + connectToActivatedSignal(item); + radio = new QuicklistMenuItemRadio (item, true); + menus2qitem_[item] = radio; - return checkmark; + return radio; } -QuicklistMenuItemRadio* -createRadioItem () +QuicklistMenuItemCheckmark* TestQuicklist::createCheckmarkItem(std::string const& label, bool enabled, bool checked) { - DbusmenuMenuitem* item = NULL; - QuicklistMenuItemRadio* radio = NULL; + DbusmenuMenuitem* item = NULL; + QuicklistMenuItemCheckmark* checkmark = NULL; item = dbusmenu_menuitem_new (); dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_LABEL, - "Radio Active"); + label.c_str()); dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, - DBUSMENU_MENUITEM_TOGGLE_RADIO); + DBUSMENU_MENUITEM_TOGGLE_CHECK); dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, - false); + enabled); dbusmenu_menuitem_property_set_int (item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, - DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); + (checked ? + DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : + DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED + )); - radio = new QuicklistMenuItemRadio (item, true); + connectToActivatedSignal(item); - g_signal_connect (item, - DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (activatedCallback), - &gResult[1]); + checkmark = new QuicklistMenuItemCheckmark (item, true); + menus2qitem_[item] = checkmark; - return radio; + return checkmark; } -QuicklistMenuItemLabel* -createLabelItem () +QuicklistMenuItemLabel* TestQuicklist::createLabelItem(std::string const& title, bool enabled) { DbusmenuMenuitem* item = NULL; QuicklistMenuItemLabel* label = NULL; @@ -134,172 +208,144 @@ createLabelItem () dbusmenu_menuitem_property_set (item, DBUSMENU_MENUITEM_PROP_LABEL, - "A Label"); + title.c_str()); dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, - true); + enabled); - label = new QuicklistMenuItemLabel (item, true); + connectToActivatedSignal(item); - g_signal_connect (item, - DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (activatedCallback), - &gResult[2]); + label = new QuicklistMenuItemLabel (item, true); + menus2qitem_[item] = label; return label; } -void -ThreadWidgetInit (nux::NThread* thread, - void* initData) +void TestQuicklist::AddItem(QuicklistMenuItem* item) { - gQuicklist = new QuicklistView (); - gQuicklist->Reference (); + if (!quicklist_) + return; - gCheckmark = createCheckmarkItem (); - gQuicklist->AddMenuItem (gCheckmark); - gRadio = createRadioItem (); - gQuicklist->AddMenuItem (gRadio); - gLabel = createLabelItem (); - gQuicklist->AddMenuItem (gLabel); + quicklist_->AddMenuItem(item); +} + +void TestQuicklist::UserInterfaceSetup() +{ + QuicklistMenuItem *item; - gQuicklist->EnableQuicklistForTesting (true); + quicklist_ = new QuicklistView(); + quicklist_->EnableQuicklistForTesting(true); + quicklist_->SetBaseXY(0, 0); - gQuicklist->SetBaseXY (0, 0); - gQuicklist->ShowWindow (true); + item = createLabelItem("Item1, normal"); + AddItem(item); + + item = createSeparatorItem(); + AddItem(item); + + item = createRadioItem("Item2, radio, checked", true, true); + AddItem(item); + + item = createRadioItem("Item3, radio, unchecked", true, false); + AddItem(item); + + item = createRadioItem("Item4, disabled radio, checked", false, true); + AddItem(item); + + item = createRadioItem("Item5, disabled radio, unchecked", false, false); + AddItem(item); + + item = createCheckmarkItem("Item6, checkmark, checked", true, true); + AddItem(item); + + item = createCheckmarkItem("Item7, checkmark, unchecked", true, false); + AddItem(item); + + item = createCheckmarkItem("Item8, disabled checkmark, checked", false, true); + AddItem(item); + + item = createCheckmarkItem("Item9, disabled checkmark, unchecked", false, false); + AddItem(item); + + item = createLabelItem("Item10, disabled", false); + AddItem(item); + + quicklist_->ShowWindow(true); + + auto wt = static_cast<nux::WindowThread*>(window_thread_); + nux::ColorLayer background (nux::Color (0x772953)); + wt->SetWindowBackgroundPaintLayer(&background); } -void -ControlThread (nux::NThread* thread, - void* data) +TestQuicklist *test_quicklist = NULL; + +void TestingThread(nux::NThread *thread, void *user_data) { - // sleep for 3 seconds - nux::SleepForMilliseconds (3000); - printf ("ControlThread successfully started\n"); - - nux::WindowThread* mainWindowThread = NUX_STATIC_CAST (nux::WindowThread*, - data); - - mainWindowThread->SetFakeEventMode (true); - Display* display = mainWindowThread->GetWindow ().GetX11Display (); - - // assemble first button-click event - XEvent buttonPressEvent; - buttonPressEvent.xbutton.type = ButtonPress; - buttonPressEvent.xbutton.serial = 0; - buttonPressEvent.xbutton.send_event = False; - buttonPressEvent.xbutton.display = display; - buttonPressEvent.xbutton.window = 0; - buttonPressEvent.xbutton.root = 0; - buttonPressEvent.xbutton.subwindow = 0; - buttonPressEvent.xbutton.time = CurrentTime; - buttonPressEvent.xbutton.x = 50; - buttonPressEvent.xbutton.y = 30; - buttonPressEvent.xbutton.x_root = 0; - buttonPressEvent.xbutton.y_root = 0; - buttonPressEvent.xbutton.state = 0; - buttonPressEvent.xbutton.button = Button1; - buttonPressEvent.xbutton.same_screen = True; - - mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread, - (XEvent*) &buttonPressEvent); - - while (!mainWindowThread->ReadyForNextFakeEvent ()) - nux::SleepForMilliseconds (10); - - XEvent buttonReleaseEvent; - buttonReleaseEvent.xbutton.type = ButtonRelease; - buttonReleaseEvent.xbutton.serial = 0; - buttonReleaseEvent.xbutton.send_event = False; - buttonReleaseEvent.xbutton.display = display; - buttonReleaseEvent.xbutton.window = 0; - buttonReleaseEvent.xbutton.root = 0; - buttonReleaseEvent.xbutton.subwindow = 0; - buttonReleaseEvent.xbutton.time = CurrentTime; - buttonReleaseEvent.xbutton.x = 50; - buttonReleaseEvent.xbutton.y = 30; - buttonReleaseEvent.xbutton.x_root = 0; - buttonReleaseEvent.xbutton.y_root = 0; - buttonReleaseEvent.xbutton.state = 0; - buttonReleaseEvent.xbutton.button = Button1; - buttonReleaseEvent.xbutton.same_screen = True; - - mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread, - (XEvent*) &buttonReleaseEvent); - - while (!mainWindowThread->ReadyForNextFakeEvent ()) - nux::SleepForMilliseconds (10); - - // assemble second button-click event - buttonPressEvent.xbutton.time = CurrentTime; - buttonPressEvent.xbutton.x = 50; - buttonPressEvent.xbutton.y = 50; - mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread, - (XEvent*) &buttonPressEvent); - while (!mainWindowThread->ReadyForNextFakeEvent ()) - nux::SleepForMilliseconds (10); - - buttonReleaseEvent.xbutton.time = CurrentTime; - buttonReleaseEvent.xbutton.x = 50; - buttonReleaseEvent.xbutton.y = 50; - mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread, - (XEvent*) &buttonReleaseEvent); - while (!mainWindowThread->ReadyForNextFakeEvent ()) - nux::SleepForMilliseconds (10); - - // assemble third button-click event - buttonPressEvent.xbutton.time = CurrentTime; - buttonPressEvent.xbutton.x = 50; - buttonPressEvent.xbutton.y = 70; - mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread, - (XEvent*) &buttonPressEvent); - while (!mainWindowThread->ReadyForNextFakeEvent ()) - nux::SleepForMilliseconds (10); - - buttonReleaseEvent.xbutton.time = CurrentTime; - buttonReleaseEvent.xbutton.x = 50; - buttonReleaseEvent.xbutton.y = 70; - mainWindowThread->PumpFakeEventIntoPipe (mainWindowThread, - (XEvent*) &buttonReleaseEvent); - while (!mainWindowThread->ReadyForNextFakeEvent ()) - nux::SleepForMilliseconds (10); - - mainWindowThread->SetFakeEventMode (false); + while (test_quicklist->ReadyToGo() == false) + { + nuxDebugMsg("Waiting to start"); + nux::SleepForMilliseconds(300); + } + + nux::SleepForMilliseconds(1300); + + auto *wnd_thread = static_cast<nux::WindowThread*>(user_data); + + NuxAutomatedTestFramework test(wnd_thread); + + test.Startup(); + + for (auto child : test_quicklist->quicklist_->GetChildren()) + { + test.ViewSendMouseMotionToCenter(child); + test.ViewSendMouseClick(child, 1); + bool activated = test_quicklist->activated_[child]; + bool should_be_activated = (child->GetItemType() != MENUITEM_TYPE_SEPARATOR && child->GetEnabled()); + + std::string msg = std::string(child->GetLabel()); + msg += should_be_activated ? " | Activated" : " | NOT Activated"; + + test.TestReportMsg(activated == should_be_activated, msg.c_str()); + nux::SleepForMilliseconds(200); + } + + if (test.WhenDoneTerminateProgram()) + { + wnd_thread->ExitMainLoop(); + } + nuxDebugMsg("Exit testing thread"); } int main (int argc, char **argv) { - nux::WindowThread* wt = NULL; - nux::SystemThread* st = NULL; - - g_type_init (); - - gtk_init (&argc, &argv); - dbus_g_thread_init (); - nux::NuxInitialize (0); - - wt = nux::CreateGUIThread (TEXT ("Unity Quicklist"), - WIN_WIDTH, - WIN_HEIGHT, - 0, - &ThreadWidgetInit, - NULL); - - st = nux::CreateSystemThread (NULL, ControlThread, wt); - if (st) - st->Start (NULL); - - wt->Run (NULL); - - gQuicklist->UnReference (); - delete st; - delete wt; - - g_assert_cmpint (gResult[0], ==, true); - g_assert_cmpint (gResult[1], ==, true); - g_assert_cmpint (gResult[2], ==, true); + gtk_init(&argc, &argv); + nuxAssertMsg(XInitThreads() > 0, "XInitThreads has failed"); + + test_quicklist = new TestQuicklist("Quicklist Test", WIN_WIDTH, WIN_HEIGHT, 100000); + test_quicklist->Startup(); + test_quicklist->UserInterfaceSetup(); + + auto *test_thread = nux::CreateSystemThread(NULL, &TestingThread, test_quicklist->GetWindowThread()); + test_thread->Start(test_quicklist); + + test_quicklist->Run(); + + g_assert_cmpint (test_quicklist->HasNthItemActivated(0), ==, true); + g_assert_cmpint (test_quicklist->HasNthItemActivated(1), ==, false); + g_assert_cmpint (test_quicklist->HasNthItemActivated(2), ==, true); + g_assert_cmpint (test_quicklist->HasNthItemActivated(3), ==, true); + g_assert_cmpint (test_quicklist->HasNthItemActivated(4), ==, false); + g_assert_cmpint (test_quicklist->HasNthItemActivated(5), ==, false); + g_assert_cmpint (test_quicklist->HasNthItemActivated(6), ==, true); + g_assert_cmpint (test_quicklist->HasNthItemActivated(7), ==, true); + g_assert_cmpint (test_quicklist->HasNthItemActivated(8), ==, false); + g_assert_cmpint (test_quicklist->HasNthItemActivated(9), ==, false); + g_assert_cmpint (test_quicklist->HasNthItemActivated(10), ==, false); + + delete test_thread; + delete test_quicklist; return 0; } diff --git a/standalone-clients/ui/TestQuicklistVisuals.cpp b/standalone-clients/ui/TestQuicklistVisuals.cpp index 03ff93672..107412668 100644 --- a/standalone-clients/ui/TestQuicklistVisuals.cpp +++ b/standalone-clients/ui/TestQuicklistVisuals.cpp @@ -18,7 +18,6 @@ #include <glib.h> #include <gtk/gtk.h> -#include <dbus/dbus-glib.h> #include "Nux/Nux.h" #include "Nux/VLayout.h" @@ -60,14 +59,12 @@ createRadioItem (const gchar* label, DBUSMENU_MENUITEM_PROP_ENABLED, enabled); - if (checked) - dbusmenu_menuitem_property_set_int (item, - DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, - DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); - else - dbusmenu_menuitem_property_set_int (item, - DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, - DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED); + dbusmenu_menuitem_property_set_int (item, + DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, + (checked ? + DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : + DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED + )); radio = new QuicklistMenuItemRadio (item, true); @@ -96,14 +93,12 @@ createCheckmarkItem (const gchar* label, DBUSMENU_MENUITEM_PROP_ENABLED, enabled); - if (checked) - dbusmenu_menuitem_property_set_int (item, - DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, - DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); - else - dbusmenu_menuitem_property_set_int (item, - DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, - DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED); + dbusmenu_menuitem_property_set_int (item, + DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, + (checked ? + DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED : + DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED + )); checkmark = new QuicklistMenuItemCheckmark (item, true); @@ -111,7 +106,7 @@ createCheckmarkItem (const gchar* label, } QuicklistMenuItemLabel* -createLabelItem (const gchar* string) +createLabelItem (const gchar* string, bool enabled = true) { DbusmenuMenuitem* item = NULL; QuicklistMenuItemLabel* label = NULL; @@ -124,7 +119,7 @@ createLabelItem (const gchar* string) dbusmenu_menuitem_property_set_bool (item, DBUSMENU_MENUITEM_PROP_ENABLED, - true); + enabled); label = new QuicklistMenuItemLabel (item, true); @@ -170,7 +165,8 @@ ThreadWidgetInit (nux::NThread* thread, gQuicklists[0]->AddMenuItem (radio); separator = createSeparatorItem (); gQuicklists[0]->AddMenuItem (separator); - label = createLabelItem ("Application Name"); + label = createLabelItem ("<b>Application Name</b>"); + label->EnableLabelMarkup(true); gQuicklists[0]->AddMenuItem (label); separator = createSeparatorItem (); gQuicklists[0]->AddMenuItem (separator); @@ -193,7 +189,8 @@ ThreadWidgetInit (nux::NThread* thread, gQuicklists[1]->AddMenuItem (checkmark); separator = createSeparatorItem (); gQuicklists[1]->AddMenuItem (separator); - label = createLabelItem ("Application Name"); + label = createLabelItem ("<b>Application Name</b>"); + label->EnableLabelMarkup(true); gQuicklists[1]->AddMenuItem (label); separator = createSeparatorItem (); gQuicklists[1]->AddMenuItem (separator); @@ -214,11 +211,12 @@ ThreadWidgetInit (nux::NThread* thread, gQuicklists[2]->AddMenuItem (separator); checkmark = createCheckmarkItem ("Option 03", false, true); gQuicklists[2]->AddMenuItem (checkmark); - checkmark = createCheckmarkItem ("Option 04", false, true); - gQuicklists[2]->AddMenuItem (checkmark); + label = createLabelItem ("Option 04", false); + gQuicklists[2]->AddMenuItem (label); separator = createSeparatorItem (); gQuicklists[2]->AddMenuItem (separator); - label = createLabelItem ("Application Name"); + label = createLabelItem ("<b>Application Name</b>"); + label->EnableLabelMarkup(true); gQuicklists[2]->AddMenuItem (label); separator = createSeparatorItem (); gQuicklists[2]->AddMenuItem (separator); @@ -227,6 +225,9 @@ ThreadWidgetInit (nux::NThread* thread, gQuicklists[2]->EnableQuicklistForTesting (true); gQuicklists[2]->SetBaseXY (45, 290); gQuicklists[2]->ShowWindow (true); + + nux::ColorLayer background (nux::Color (0x772953)); + static_cast<nux::WindowThread*>(thread)->SetWindowBackgroundPaintLayer(&background); } int @@ -234,10 +235,7 @@ main (int argc, char **argv) { nux::WindowThread* wt = NULL; - g_type_init (); - gtk_init (&argc, &argv); - dbus_g_thread_init (); nux::NuxInitialize (0); wt = nux::CreateGUIThread (TEXT ("Unity visual Quicklist-test"), diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 840f0fa51..1759638f2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -99,6 +99,8 @@ if (GTEST_FOUND AND # The service that provides DBus services to test against add_executable(test-gtest-service + test_service_hud.c + test_service_hud.h test_service_lens.c test_service_lens.h test_service_main.c @@ -183,7 +185,8 @@ if (GTEST_FOUND AND test_utils.h test_ratings_filter.cpp test_results.cpp - ) + test_hud.cpp + ) target_link_libraries(test-gtest-dbus ${GTEST_BOTH_LIBRARIES}) add_test(UnityGTestDBus test-gtest-dbus) add_dependencies(test-gtest-dbus unity-core-${UNITY_API_VERSION} test-gtest-service) diff --git a/tests/autopilot/autopilot/emulators/X11.py b/tests/autopilot/autopilot/emulators/X11.py index 8ee7c0d65..a668d529c 100644 --- a/tests/autopilot/autopilot/emulators/X11.py +++ b/tests/autopilot/autopilot/emulators/X11.py @@ -2,17 +2,18 @@ # Copyright 2010 Canonical # Author: Alex Launi # -# 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 +# 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 script is designed to run unity in a test drive manner. It will drive +# This script is designed to run unity in a test drive manner. It will drive # X and test the GL calls that Unity makes, so that we can easily find out if # we are triggering graphics driver/X bugs. -""" -A collection of emulators for X11 - namely keyboards and mice. In the future we may -also need other devices. +"""A collection of emulators for X11 - namely keyboards and mice. + +In the future we may also need other devices. + """ @@ -24,21 +25,12 @@ from Xlib.display import Display from Xlib.ext.xtest import fake_input import gtk.gdk +_PRESSED_KEYS = [] +_DISPLAY = Display() + + class Keyboard(object): - '''Wrapper around xlib to make faking keyboard input possible''' - _lame_hardcoded_keycodes = { - 'E' : 9, # escape - 'A' : 64, - 'C' : 37, - 'S' : 50, - 'T' : 23, - 'W' : 133, - 'U' : 111, # up arrow - 'D' : 116, # down arrow - 'L' : 113, # left arrow - 'R' : 114, # right arrow - '1' : 67 # f1 - } + """Wrapper around xlib to make faking keyboard input possible.""" _special_X_keysyms = { ' ' : "space", @@ -79,69 +71,110 @@ class Keyboard(object): '}' : "braceright", '~' : "asciitilde" } - + + _keysym_translations = { + 'Control' : 'Control_L', + 'Ctrl' : 'Control_L', + 'Alt' : 'Alt_L', + 'Super' : 'Super_L', + 'Shift' : 'Shift_L', + 'Enter' : 'Return', + } + def __init__(self): - self._display = Display() + self.shifted_keys = [k[1] for k in _DISPLAY._keymap_codes if k] - def press(self, keys): - """ - Send key press events for every key in the 'keys' string. - """ - self.__perform_on_keys(keys, X.KeyPress) - sleep(0.2) + def press(self, keys, delay=0.2): + """Send key press events only. + + The 'keys' argument must be a string of keys you want + pressed. For example: + + press('Alt+F2') + + presses the 'Alt' and 'F2' keys. - def release(self, keys): - """ - Send key release events for every key in the 'keys' string. """ - self.__perform_on_keys(keys, X.KeyRelease) - sleep(0.2) - - def press_and_release(self, keys): + if not isinstance(keys, basestring): + raise TypeError("'keys' argument must be a string.") + self.__perform_on_keys(self.__translate_keys(keys), X.KeyPress) + sleep(delay) + + def release(self, keys, delay=0.2): + """Send key release events only. + + The 'keys' argument must be a string of keys you want + released. For example: + + release('Alt+F2') + + releases the 'Alt' and 'F2' keys. + """ - Send key press events for every key in the 'keys' string, then send - key release events for every key in the 'keys' string. + if not isinstance(keys, basestring): + raise TypeError("'keys' argument must be a string.") + self.__perform_on_keys(self.__translate_keys(keys), X.KeyRelease) + sleep(delay) + + def press_and_release(self, keys, delay=0.2): + """Press and release all items in 'keys'. + + This is the same as calling 'press(keys);release(keys)'. + + The 'keys' argument must be a string of keys you want + pressed and released.. For example: + + press_and_release('Alt+F2']) + + presses both the 'Alt' and 'F2' keys, and then releases both keys. - This method is not appropriate for simulating a user typing a string - of text, since it presses all the keys, and then releases them all. """ - self.press(keys) - self.release(keys) - def type(self, keys): + self.press(keys, delay) + self.release(keys, delay) + + def type(self, string, delay=0.1): + """Simulate a user typing a string of text. + + Only 'normal' keys can be typed with this method. Control characters + (such as 'Alt' will be interpreted as an 'A', and 'l', and a 't'). + """ - Simulate a user typing the keys specified in 'keys'. + if not isinstance(string, basestring): + raise TypeError("'keys' argument must be a string.") + for key in string: + self.press(key, delay) + self.release(key, delay) + + @staticmethod + def cleanup(): + """Generate KeyRelease events for any un-released keys. + + Make sure you call this at the end of any test to release + any keys that were pressed and not released. - Each key will be pressed and released before the next key is processed. If - you need to simulate multiple keys being pressed at the same time, use the - 'press_and_release' method above. """ - for key in keys: - self.press(key) - self.release(key) + for keycode in _PRESSED_KEYS: + print "Releasing key: %r" % (keycode) + fake_input(_DISPLAY, X.KeyRelease, keycode) def __perform_on_keys(self, keys, event): - control_key = False keycode = 0 shift_mask = 0 - for index in range(len(keys)): - key = keys[index] - if control_key: - keycode = self._lame_hardcoded_keycodes[key] - shift_mask = 0 - control_key = False - elif index < len(keys) and key == '^' and keys[index+1] in self._lame_hardcoded_keycodes: - control_key = True - continue - else: - keycode, shift_mask = self.__char_to_keycode(key) + for key in keys: + keycode, shift_mask = self.__char_to_keycode(key) if shift_mask != 0: - fake_input(self._display, event, 50) + fake_input(_DISPLAY, event, 50) + + if event == X.KeyPress: + _PRESSED_KEYS.append(keycode) + elif event == X.KeyRelease: + _PRESSED_KEYS.remove(keycode) - fake_input(self._display, event, keycode) - self._display.sync() + fake_input(_DISPLAY, event, keycode) + _DISPLAY.sync() def __get_keysym(self, key) : keysym = XK.string_to_keysym(key) @@ -151,70 +184,68 @@ class Keyboard(object): # the subsequent display.keysym_to_keycode("numbersign") is 0. keysym = XK.string_to_keysym(self._special_X_keysyms[key]) return keysym - + def __is_shifted(self, key) : - if key.isupper() : - return True - if "~!@#$%^&*()_+{}|:\"<>?".find(key) >= 0 : - return True - return False + return len(key) == 1 and ord(key) in self.shifted_keys def __char_to_keycode(self, key) : keysym = self.__get_keysym(key) - keycode = self._display.keysym_to_keycode(keysym) + keycode = _DISPLAY.keysym_to_keycode(keysym) if keycode == 0 : print "Sorry, can't map", key - + if (self.__is_shifted(key)) : shift_mask = X.ShiftMask else : shift_mask = 0 - return keycode, shift_mask + def __translate_keys(self, key_string): + return [self._keysym_translations.get(k,k) for k in key_string.split('+')] + + class Mouse(object): - '''Wrapper around xlib to make moving the mouse easier''' - - def __init__(self): - self._display = Display() + """Wrapper around xlib to make moving the mouse easier.""" @property def x(self): + """Mouse position X coordinate.""" return self.position()[0] @property def y(self): + """Mouse position Y coordinate.""" return self.position()[1] - + def press(self, button=1): - '''Press mouse button at current mouse location''' - fake_input(self._display, X.ButtonPress, button) - self._display.sync() - + """Press mouse button at current mouse location.""" + fake_input(_DISPLAY, X.ButtonPress, button) + _DISPLAY.sync() + def release(self, button=1): - '''Releases mouse button at current mouse location''' - fake_input(self._display, X.ButtonRelease, button) - self._display.sync() - + """Releases mouse button at current mouse location.""" + fake_input(_DISPLAY, X.ButtonRelease, button) + _DISPLAY.sync() + def click(self, button=1): - '''Click mouse at current location''' + """Click mouse at current location.""" self.press(button) sleep(0.25) self.release(button) - + def move(self, x, y, animate=True, rate=100, time_between_events=0.001): '''Moves mouse to location (x, y, pixels_per_event, time_between_event)''' def perform_move(x, y, sync): - fake_input(self._display, X.MotionNotify, sync, X.CurrentTime, X.NONE, x=x, y=y) - self._display.sync() + fake_input(_DISPLAY, X.MotionNotify, sync, X.CurrentTime, X.NONE, x=x, y=y) + _DISPLAY.sync() sleep(time_between_events) if not animate: perform_move(x, y, False) - + dest_x, dest_y = x, y curr_x, curr_y = self.position() - + # calculate a path from our current position to our destination dy = float(curr_y - dest_y) dx = float(curr_x - dest_x) @@ -226,24 +257,25 @@ class Mouse(object): target_x = min(curr_x + xscale, dest_x) if dest_x > curr_x else max(curr_x + xscale, dest_x) perform_move(target_x - curr_x, 0, True) curr_x = target_x; - + if (curr_y != dest_y): yscale = rate if dest_y > curr_y else -rate while (curr_y != dest_y): target_y = min(curr_y + yscale, dest_y) if dest_y > curr_y else max(curr_y + yscale, dest_y) perform_move(0, target_y - curr_y, True) curr_y = target_y - + def position(self): - '''Returns the current position of the mouse pointer''' - coord = self._display.screen().root.query_pointer()._data + """Returns the current position of the mouse pointer.""" + coord = _DISPLAY.screen().root.query_pointer()._data x, y = coord["root_x"], coord["root_y"] return x, y - - def reset(self): - self.move(16, 13, animate=False) - self.click() - self.move(800, 500, animate=False) + + @staticmethod + def cleanup(): + """Put mouse in a known safe state.""" + sg = ScreenGeometry() + sg.move_mouse_to_monitor(0) class ScreenGeometry: diff --git a/tests/autopilot/autopilot/emulators/bamf.py b/tests/autopilot/autopilot/emulators/bamf.py new file mode 100644 index 000000000..461e6eb22 --- /dev/null +++ b/tests/autopilot/autopilot/emulators/bamf.py @@ -0,0 +1,341 @@ +# Copyright 2011 Canonical +# Author: Thomi Richards +# +# 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. + +"Various classes for interacting with BAMF." + +import dbus +from dbus.mainloop.glib import DBusGMainLoop +import gio +import gobject +from Xlib import display, X, protocol + +__all__ = ["Bamf", + "BamfApplication", + "BamfWindow", + ] + + +_BAMF_BUS_NAME = 'org.ayatana.bamf' +DBusGMainLoop(set_as_default=True) +_session_bus = dbus.SessionBus() +_X_DISPLAY = display.Display() + + +def _filter_user_visible(win): + """Filter out non-user-visible objects. + + In some cases the DBus method we need to call hasn't been registered yet, + in which case we do the safe thing and return False. + + """ + try: + return win.user_visible + except dbus.DBusException: + return False + + +class Bamf: + """High-level class for interacting with Bamf from within a test. + + Use this class to inspect the state of running applications and open + windows. + + """ + + def __init__(self): + matcher_path = '/org/ayatana/bamf/matcher' + self.matcher_interface_name = 'org.ayatana.bamf.matcher' + self.matcher_proxy = _session_bus.get_object(_BAMF_BUS_NAME, matcher_path) + self.matcher_interface = dbus.Interface(self.matcher_proxy, self.matcher_interface_name) + + def get_running_applications(self, user_visible_only=True): + """Get a list of the currently running applications. + + If user_visible_only is True (the default), only applications + visible to the user in the switcher will be returned. + + """ + apps = [BamfApplication(p) for p in self.matcher_interface.RunningApplications()] + if user_visible_only: + return filter(_filter_user_visible, apps) + return apps + + def get_running_applications_by_title(self, app_title): + """Return a list of applications that have the title `app_title`. + + This method may return an empty list, if no applications + are found with the specified title. + + """ + return [a for a in self.get_running_applications() if a.name == app_title] + + def get_open_windows(self, user_visible_only=True): + """Get a list of currently open windows. + + If user_visible_only is True (the default), only applications + visible to the user in the switcher will be returned. + + """ + + windows = [BamfWindow(w) for w in self.matcher_interface.WindowPaths()] + if user_visible_only: + return filter(_filter_user_visible, windows) + return windows + + def get_open_windows_by_title(self, win_title): + """Get a list of all open windows with a specific window title. + + This method may return an empty list if no currently open windows have + the specified title. + + """ + return [w for w in self.get_open_windows() if w.title == win_title] + + def application_is_running(self, app_name): + """Detect if an application with a given name is currently running. + + 'app_name' is the name of the application you are looking for. + """ + return app_name in [a.name for a in self.get_running_applications()] + + def wait_until_application_is_running(self, app_name, timeout): + """Wait until a given application is running. + + 'app_name' is the name of the application. + 'timeout' is the maximum time to wait, in seconds. If set to + something less than 0, this method will wait forever. + + This method returns true once the application is found, or false + if the application was not found until the timeout was reached. + """ + # python workaround since you can't assign to variables in the enclosing scope: + # see on_timeout_reached below... + found_app = [True] + + # maybe the app is running already? + if not self.application_is_running(app_name): + wait_forever = timeout < 0 + gobject_loop = gobject.MainLoop() + # No, so define a callback to watch the ViewOpened signal: + def on_view_added(bamf_path, name): + if bamf_path.split('/')[-1].startswith('application'): + app = BamfApplication(bamf_path) + if app.name == app_name: + gobject_loop.quit() + + # ...and one for when the user-defined timeout has been reached: + def on_timeout_reached(): + gobject_loop.quit() + found_app[0] = False + return False + + # need a timeout? if so, connect it: + if not wait_forever: + gobject.timeout_add(timeout * 1000, on_timeout_reached) + # connect signal handler: + _session_bus.add_signal_receiver(on_view_added, 'ViewOpened') + # pump the gobject main loop until either the correct signal is emitted, or the + # timeout happens. + gobject_loop.run() + + return found_app[0] + + def launch_application(self, desktop_file, wait=True): + """Launch an application by specifying a desktop file. + + Returns the Gobject process object. if wait is True (the default), + this method will not return until an instance of this application + appears in the BAMF application list. + """ + proc = gio.unix.DesktopAppInfo(desktop_file) + proc.launch() + if wait: + self.wait_until_application_is_running(proc.get_name(), -1) + return proc + + +class BamfApplication: + """Represents an application, with information as returned by Bamf. + + Don't instantiate this class yourself. instead, use the methods as + provided by the Bamf class. + + """ + def __init__(self, bamf_app_path): + self.bamf_app_path = bamf_app_path + try: + self._app_proxy = _session_bus.get_object(_BAMF_BUS_NAME, bamf_app_path) + self._view_iface = dbus.Interface(self._app_proxy, 'org.ayatana.bamf.view') + except dbus.DBusException, e: + e.message += 'bamf_app_path=%r' % (bamf_app_path) + raise + + @property + def name(self): + """Get the application name.""" + return self._view_iface.Name() + + @property + def is_active(self): + """Is the application active (i.e.- has keyboard focus)?""" + return self._view_iface.IsActive() + + @property + def is_urgent(self): + """Is the application currently signalling urgency?""" + return self._view_iface.IsUrgent() + + @property + def user_visible(self): + """Is this application visible to the user? + + Some applications (such as the panel) are hidden to the user but will + still be returned by bamf. + + """ + return self._view_iface.UserVisible() + + def get_windows(self): + """Get a list of the application windows.""" + return [BamfWindow(w) for w in self._view_iface.Children()] + + def __repr__(self): + return "<BamfApplication '%s'>" % (self.name) + + +class BamfWindow: + """Represents an application window, as returned by Bamf. + + Don't instantiate this class yourself. Instead, use the appropriate methods + in BamfApplication. + + """ + def __init__(self, window_path): + self._bamf_win_path = window_path + self._app_proxy = _session_bus.get_object(_BAMF_BUS_NAME, window_path) + self._window_iface = dbus.Interface(self._app_proxy, 'org.ayatana.bamf.window') + self._view_iface = dbus.Interface(self._app_proxy, 'org.ayatana.bamf.view') + + self._xid = int(self._window_iface.GetXid()) + self._x_root_win = _X_DISPLAY.screen().root + self._x_win = _X_DISPLAY.create_resource_object('window', self._xid) + + + @property + def x_id(self): + """Get the X11 Window Id.""" + return self._xid + + @property + def title(self): + """Get the window title. + + This may be different from the application name. + + """ + return self._getProperty('_NET_WM_NAME') + + @property + def geometry(self): + """Get the geometry for this window. + + Returns a tuple containing (x, y, width, height). + + """ + + geometry = self._x_win.get_geometry() + return (geometry.x, geometry.y, geometry.width, geometry.height) + + @property + def is_maximized(self): + """Is the window maximized? + + Maximized in this case means both maximized + vertically and horizontally. If a window is only maximized in one + direction it is not considered maximized. + + """ + win_state = self._get_window_states() + return '_NET_WM_STATE_MAXIMIZED_VERT' in win_state and \ + '_NET_WM_STATE_MAXIMIZED_HORZ' in win_state + + @property + def application(self): + """Get the application that owns this window. + + This method may return None if the window does not have an associated + application. The 'desktop' window is one such example. + + """ + # BAMF returns a list of parents since some windows don't have an + # associated application. For these windows we return none. + parents = self._view_iface.Parents() + if parents: + return BamfApplication(parents[0]) + else: + return None + + @property + def user_visible(self): + """Is this window visible to the user in the switcher?""" + return self._view_iface.UserVisible() + + @property + def is_hidden(self): + """Is this window hidden? + + Windows are hidden when the 'Show Desktop' mode is activated. + + """ + win_state = self._get_window_states() + return '_NET_WM_STATE_HIDDEN' in win_state + + @property + def is_valid(self): + """Is this window object valid? + + Invalid windows are caused by windows closing during the construction of + this object instance. + + """ + return not self._x_win is None + + def close(self): + """Close the window.""" + + self._setProperty('_NET_CLOSE_WINDOW', [0, 0]) + + def __repr__(self): + return "<BamfWindow '%s'>" % (self.title if self._x_win else str(self._xid)) + + def _getProperty(self, _type): + """Get an X11 property. + + _type is a string naming the property type. win is the X11 window object. + + """ + atom = self._x_win.get_full_property(_X_DISPLAY.get_atom(_type), X.AnyPropertyType) + if atom: return atom.value + + def _setProperty(self, _type, data, mask=None): + if type(data) is str: + dataSize = 8 + else: + data = (data+[0]*(5-len(data)))[:5] + dataSize = 32 + + ev = protocol.event.ClientMessage(window=self._x_win, client_type=_X_DISPLAY.get_atom(_type), data=(dataSize, data)) + + if not mask: + mask = (X.SubstructureRedirectMask|X.SubstructureNotifyMask) + self._x_root_win.send_event(ev, event_mask=mask) + _X_DISPLAY.sync() + + def _get_window_states(self): + """Return a list of strings representing the current window state.""" + + return map(_X_DISPLAY.get_atom_name, self._getProperty('_NET_WM_STATE')) diff --git a/tests/autopilot/autopilot/emulators/unity.py b/tests/autopilot/autopilot/emulators/unity.py index dfcd89a14..4768827e1 100644 --- a/tests/autopilot/autopilot/emulators/unity.py +++ b/tests/autopilot/autopilot/emulators/unity.py @@ -10,9 +10,7 @@ # X and test the GL calls that Unity makes, so that we can easily find out if # we are triggering graphics driver/X bugs. -""" -A collection of emulators that make it easier to interact with Unity. -""" +"""A collection of emulators that make it easier to interact with Unity.""" from compizconfig import Setting from compizconfig import Plugin @@ -23,12 +21,12 @@ from autopilot.emulators.X11 import Keyboard, Mouse, ScreenGeometry from autopilot.globals import global_context class Unity(object): - ''' - High level class to abstract interactions with the unity shell. + """High level class to abstract interactions with the unity shell. - This class should not be used directly. Instead, use one of the derived classes to - interact with a different piece of the Unity system. - ''' + This class should not be used directly. Instead, use one of the derived + classes to interact with a different piece of the Unity system. + + """ def __init__(self): ## TODO @@ -48,16 +46,12 @@ class Unity(object): self.INTROSPECTION_IFACE) def get_state(self, piece='/Unity'): - ''' - returns a full dump of unity's state via the introspection interface - ''' + """Returns a full dump of unity's state.""" return self._introspection_iface.GetState(piece) class Launcher(Unity): - """ - Interact with the unity Launcher. - """ + """Interact with the unity Launcher.""" def __init__(self): super(Launcher, self).__init__() @@ -70,11 +64,14 @@ class Launcher(Unity): self.hide_timeout = 1 self.grabbed = False + state = self.__get_state(0) + self.icon_width = int(state['icon-size']) + def move_mouse_to_right_of_launcher(self, monitor): (x, y, w, h) = self.launcher_geometry(monitor) - self._mouse.move(x + w + 15, y + h / 2, False) + self._mouse.move(x + w + 10, y + h / 2, False) sleep(self.show_timeout) - + def move_mouse_over_launcher(self, monitor): (x, y, w, h) = self.launcher_geometry(monitor) self._screen.move_mouse_to_monitor(monitor); @@ -82,57 +79,57 @@ class Launcher(Unity): def reveal_launcher(self, monitor): (x, y, w, h) = self.launcher_geometry(monitor) - self._mouse.move(x - 920, y + h / 2, True, 5, .002) + self._mouse.move(x - 1200, y + h / 2) sleep(self.show_timeout) def keyboard_reveal_launcher(self): - self._keyboard.press('^W') + self._keyboard.press('Super') sleep(1) - + def keyboard_unreveal_launcher(self): - self._keyboard.release('^W') + self._keyboard.release('Super') sleep(1) def grab_switcher(self): - self._keyboard.press_and_release('^A^1') + self._keyboard.press_and_release('Alt+F1') self.grabbed = True def switcher_enter_quicklist(self): if self.grabbed: - self._keyboard.press_and_release('^R') + self._keyboard.press_and_release('Right') def switcher_exit_quicklist(self): if self.grabbed: - self._keyboard.press_and_release('^L') + self._keyboard.press_and_release('Left') def start_switcher(self): - self._keyboard.press('^W^T') - self._keyboard.release('^T') + self._keyboard.press('Super+Tab') + self._keyboard.release('Tab') sleep(1) def end_switcher(self, cancel): if cancel: - self._keyboard.press_and_release('^E') + self._keyboard.press_and_release('Escape') if self.grabbed != True: - self._keyboard.release('^W') + self._keyboard.release('Super') else: if self.grabbed: self._keyboard.press_and_release('\n') else: - self._keyboard.release('^W') + self._keyboard.release('Super') self.grabbed = False def switcher_next(self): if self.grabbed: - self._keyboard.press_and_release('^D') + self._keyboard.press_and_release('Down') else: - self._keyboard.press_and_release('^T') + self._keyboard.press_and_release('Tab') def switcher_prev(self): if self.grabbed: - self._keyboard.press_and_release('^U') + self._keyboard.press_and_release('Up') else: - self._keyboard.press_and_release('^S^T') + self._keyboard.press_and_release('Shift+Tab') def quicklist_open(self, monitor): state = self.__get_state(monitor) @@ -176,76 +173,96 @@ class Launcher(Unity): # get the state for the 'launcher' piece return super(Launcher, self).get_state('/Unity/LauncherController/Launcher[monitor=%s]' % (monitor))[0] + def get_launcher_icons(self): + """Get a list of launcher icons in this launcher.""" + icons = self.get_state("//Launcher/LauncherIcon") + return [LauncherIcon(icon_dict) for icon_dict in icons] -class UnityLauncherIconTooltip(Unity): - """ - Interact with the Launcher Icon Tooltips? - """ - RENDER_TIMEOUT_MS = 0 - SHOW_X_POS = 0 - SHOW_Y_POS = 32 - HIDE_X_POS = 64 - HIDE_Y_POS = 0 - WIDTH = 0 - HEIGHT = 0 - - def __init__(self, TooltipText=None): - self.mouse = Mouse() + def click_launcher_icon(self, icon, monitor=0, button=1): + """Move the mouse over the launcher icon, and click it.""" + self.reveal_launcher(monitor) + self._mouse.move(icon.x, icon.y + (self.icon_width / 2)) + self._mouse.click(button) + self.move_mouse_to_right_of_launcher(monitor) - def setUp(self): - self.mouse.move(200, 200) +class LauncherIcon: + """Holds information about a launcher icon. - def show_on_mouse_hover(self): - self.mouse.move(self.SHOW_X_POS, self.SHOW_Y_POS) - sleep(self.RENDER_TIMEOUT_MS) + Do not instantiate an instance of this class yourself. Instead, use the + appropriate methods in the Launcher class instead. - def hide_on_mouse_out(self): - self.mouse.move(self.HIDE_X_POS, self.HIDE_Y_POS) - - def isRendered(self): - return False + """ + def __init__(self, icon_dict): + self.tooltip_text = icon_dict['tooltip-text'] + self.x = icon_dict['x'] + self.y = icon_dict['y'] + self.num_windows = icon_dict['related-windows'] + self.visible = icon_dict['quirk-visible'] + self.active = icon_dict['quirk-active'] + self.running = icon_dict['quirk-running'] + self.presented = icon_dict['quirk-presented'] + self.urgent = icon_dict['quirk-urgent'] class Switcher(Unity): - """ - Interact with the Unity switcher. - """ + """Interact with the Unity switcher.""" def __init__(self): super(Switcher, self).__init__() def initiate(self): - self._keyboard.press('^A^T') - self._keyboard.release('^T') + """Start the switcher with alt+tab.""" + self._keyboard.press('Alt') + self._keyboard.press_and_release('Tab') + sleep(1) def initiate_detail_mode(self): - self._keyboard.press('^A`') - self._keyboard.release('`') + """Start detail mode with alt+`""" + self._keyboard.press('Alt') + self._keyboard.press_and_release('`') def terminate(self): - self._keyboard.release('^A') + """Stop switcher.""" + self._keyboard.release('Alt') def next_icon(self): - self._keyboard.press_and_release('^T') + """Move to the next application.""" + self._keyboard.press_and_release('Tab') def previous_icon(self): - self._keyboard.press_and_release('^S^T') + """Move to the previous application.""" + self._keyboard.press_and_release('Shift+Tab') def show_details(self): + """Show detail mode.""" self._keyboard.press_and_release('`') def hide_details(self): - self._keyboard.press_and_release('^U') + """Hide detail mode.""" + self._keyboard.press_and_release('Up') def next_detail(self): + """Move to next detail in the switcher.""" self._keyboard.press_and_release('`') def previous_detail(self): - self._keyboard.press_and_release('^S`') + """Move to the previous detail in the switcher.""" + self._keyboard.press_and_release('Shift+`') def __get_icon(self, index): - import ipdb; ipdb.set_trace() - return self.get_state('/Unity/SwitcherController/SwitcherModel')[0]['children-of-men'][index][1][0] + return self.__get_model()['Children'][index][1][0] + + @property + def current_icon(self): + """Get the currently-selected icon.""" + if not self.get_is_visible: + return None + model = self.__get_model() + sel_idx = self.get_selection_index() + try: + return LauncherIcon(model['Children'][sel_idx][1]) + except KeyError: + return None def get_icon_name(self, index): return self.__get_icon(index)['tooltip-text'] @@ -257,56 +274,93 @@ class Switcher(Unity): return None def get_model_size(self): - return len(self.get_state('/Unity/SwitcherController/SwitcherModel')[0]['children-of-men']) + return len(self.__get_model()['Children']) def get_selection_index(self): - return int(self.get_state('/Unity/SwitcherController/SwitcherModel')[0]['selection-index']) + return int(self.__get_model()['selection-index']) def get_last_selection_index(self): - return bool(self.get_state('/Unity/SwitcherController/SwitcherModel')[0]['last-selection-index']) + return bool(self.__get_model()['last-selection-index']) def get_is_visible(self): - return bool(self.get_state('/Unity/SwitcherController')[0]['visible']) + return bool(self.__get_controller()['visible']) + + def __get_model(self): + return self.get_state('/Unity/SwitcherController/SwitcherModel')[0] + + def __get_controller(self): + return self.set_state('/unity/SwitcherController')[0] class Dash(Unity): - """ - An emulator class that makes it easier to interact with the unity dash. - """ - - def __init__(self): - self.plugin = Plugin(global_context, "unityshell") - self.setting = Setting(self.plugin, "show_launcher") - super(Dash, self).__init__() - - def toggle_reveal(self): - """ - Reveals the dash if it's currently hidden, hides it otherwise. - """ - self._keyboard.press_and_release("^W") - sleep(1) - - def ensure_visible(self): - """ - Ensures the dash is visible. - """ - if not self.get_is_visible(): - self.toggle_reveal(); - - def ensure_hidden(self): - """ - Ensures the dash is hidden. - """ - if self.get_is_visible(): - self.toggle_reveal(); - - def get_is_visible(self): - """ - Is the dash visible? - """ + """ + An emulator class that makes it easier to interact with the unity dash. + """ + + def __init__(self): + self.plugin = Plugin(global_context, "unityshell") + self.setting = Setting(self.plugin, "show_launcher") + super(Dash, self).__init__() + + def toggle_reveal(self): + """ + Reveals the dash if it's currently hidden, hides it otherwise. + """ + self._keyboard.press_and_release("Super") + sleep(1) + + def ensure_visible(self): + """ + Ensures the dash is visible. + """ + if not self.get_is_visible(): + self.toggle_reveal(); + + def ensure_hidden(self): + """ + Ensures the dash is hidden. + """ + if self.get_is_visible(): + self.toggle_reveal(); + + def get_is_visible(self): + """ + Is the dash visible? + """ return bool(self.get_state("/Unity/DashController")[0]["visible"]) - def get_search_string(self): - """ - Return the current dash search bar search string. - """ + def get_search_string(self): + """ + Return the current dash search bar search string. + """ return unicode(self.get_state("//SearchBar")[0]['search_string']) + + def get_current_lens(self): + """Returns the id of the current lens. + + For example, the default lens is 'home.lens', the run-command lens is + 'commands.lens'. + + """ + return unicode(self.get_state("//DashController/DashView/LensBar")[0]['active-lens']) + + def reveal_application_lens(self): + """Reveal the application lense.""" + self._keyboard.press('Super') + self._keyboard.press_and_release("a") + self._keyboard.release('Super') + + def reveal_music_lens(self): + """Reveal the music lense.""" + self._keyboard.press('Super') + self._keyboard.press_and_release("m") + self._keyboard.release('Super') + + def reveal_file_lens(self): + """Reveal the file lense.""" + self._keyboard.press('Super') + self._keyboard.press_and_release("f") + self._keyboard.release('Super') + + def reveal_command_lens(self): + """Reveal the 'run command' lens.""" + self._keyboard.press_and_release('Alt+F2') diff --git a/tests/autopilot/autopilot/tests/__init__.py b/tests/autopilot/autopilot/tests/__init__.py index f08df6284..17904e51f 100644 --- a/tests/autopilot/autopilot/tests/__init__.py +++ b/tests/autopilot/autopilot/tests/__init__.py @@ -1,3 +1,14 @@ """ Autopilot tests for Unity. -""" \ No newline at end of file +""" + +from testtools import TestCase +from autopilot.emulators.X11 import Keyboard, Mouse + +class AutopilotTestCase(TestCase): + """Wrapper around testtools.TestCase that takes care of some cleaning.""" + + def tearDown(self): + Keyboard.cleanup() + Mouse.cleanup() + super(AutopilotTestCase, self).tearDown() diff --git a/tests/autopilot/autopilot/tests/test_dash.py b/tests/autopilot/autopilot/tests/test_dash.py index ceb20252a..e9f6b4024 100644 --- a/tests/autopilot/autopilot/tests/test_dash.py +++ b/tests/autopilot/autopilot/tests/test_dash.py @@ -1,25 +1,33 @@ -from testtools import TestCase +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2010 Canonical +# Author: Alex Launi +# +# 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. + from time import sleep from autopilot.emulators.unity import Dash from autopilot.emulators.X11 import Keyboard +from autopilot.tests import AutopilotTestCase from autopilot.glibrunner import GlibRunner -class DashTests(TestCase): - """ - Test the unity Dash. - """ +class DashTests(AutopilotTestCase): + """Test the unity Dash.""" run_test_with = GlibRunner - + def setUp(self): super(DashTests, self).setUp() self.dash = Dash() + def tearDown(self): + super(DashTests, self).tearDown() + self.dash.ensure_hidden() + def test_dash_reveal(self): - """ - Ensure we can show and hide the dash. - """ + """Ensure we can show and hide the dash.""" self.dash.ensure_hidden() self.assertFalse(self.dash.get_is_visible()) @@ -29,9 +37,7 @@ class DashTests(TestCase): self.assertFalse(self.dash.get_is_visible()) def test_dash_keyboard_focus(self): - """ - Dash must put keyboard focus on the search bar at all times. - """ + """Dash must put keyboard focus on the search bar at all times.""" self.dash.ensure_hidden() self.assertFalse(self.dash.get_is_visible()) @@ -43,5 +49,30 @@ class DashTests(TestCase): self.dash.toggle_reveal() self.assertFalse(self.dash.get_is_visible()) + def test_application_lens_shortcut(self): + """Application lense must reveal when Super+a is pressed.""" + self.dash.ensure_hidden() + self.dash.reveal_application_lens() + self.assertEqual(self.dash.get_current_lens(), u'applications.lens') + + def test_music_lens_shortcut(self): + """Music lense must reveal when Super+w is pressed.""" + self.dash.ensure_hidden() + self.dash.reveal_music_lens() + self.assertEqual(self.dash.get_current_lens(), u'music.lens') + + def test_file_lens_shortcut(self): + """File lense must reveal when Super+f is pressed.""" + self.dash.ensure_hidden() + self.dash.reveal_file_lens() + self.assertEqual(self.dash.get_current_lens(), u'files.lens') + + def test_command_lens_shortcut(self): + """Run Command lens must reveat on alt+F2.""" + self.dash.ensure_hidden() + self.dash.reveal_command_lens() + self.assertEqual(self.dash.get_current_lens(), u'commands.lens') + + + - diff --git a/tests/autopilot/autopilot/tests/test_launcher.py b/tests/autopilot/autopilot/tests/test_launcher.py index c53831448..b3e532c1a 100644 --- a/tests/autopilot/autopilot/tests/test_launcher.py +++ b/tests/autopilot/autopilot/tests/test_launcher.py @@ -1,13 +1,15 @@ -from testtools import TestCase from testtools.matchers import Equals from testtools.matchers import LessThan +from autopilot.tests import AutopilotTestCase from autopilot.emulators.unity import Launcher from autopilot.glibrunner import GlibRunner from time import sleep -class LauncherTests(TestCase): + +class LauncherTests(AutopilotTestCase): + """Test the launcher.""" run_test_with = GlibRunner def setUp(self): @@ -90,6 +92,9 @@ class LauncherTests(TestCase): def test_reveal_on_mouse_to_edge(self): """Tests reveal of launchers by mouse pressure.""" + # XXX: re-enable test when launcher reeal behavior is no longer resolution-dependant. + self.skipTest("Launcher reveal behavior is resolution dependant.") + num_launchers = self.server.num_launchers() for x in range(num_launchers): @@ -98,8 +103,11 @@ class LauncherTests(TestCase): self.assertThat(self.server.is_showing(x), Equals(True)) def test_reveal_with_mouse_under_launcher(self): - """Tests that the launcher hides properly if the + """Tests that the launcher hides properly if the mouse is under the launcher when it is revealed.""" + # XXX: re-enable test when launcher reeal behavior is no longer resolution-dependant. + self.skipTest("Launcher reveal behavior is resolution dependant.") + num_launchers = self.server.num_launchers() for x in range(num_launchers): @@ -107,9 +115,12 @@ class LauncherTests(TestCase): self.server.keyboard_reveal_launcher() self.server.keyboard_unreveal_launcher() self.assertThat(self.server.is_showing(x), Equals(False)) - + def test_reveal_does_not_hide_again(self): """Tests reveal of launchers by mouse pressure to ensure it doesn't automatically hide again.""" + # XXX: re-enable test when launcher reeal behavior is no longer resolution-dependant. + self.skipTest("Launcher reveal behavior is resolution dependant.") + num_launchers = self.server.num_launchers() for x in range(num_launchers): @@ -117,5 +128,5 @@ class LauncherTests(TestCase): self.server.reveal_launcher(x) sleep(2) self.assertThat(self.server.is_showing(x), Equals(True)) - + diff --git a/tests/autopilot/autopilot/tests/test_showdesktop.py b/tests/autopilot/autopilot/tests/test_showdesktop.py new file mode 100644 index 000000000..dedac49b9 --- /dev/null +++ b/tests/autopilot/autopilot/tests/test_showdesktop.py @@ -0,0 +1,138 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2010 Canonical +# Author: Thomi Richards +# +# 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. + +from time import sleep +from subprocess import call + +from autopilot.emulators.bamf import Bamf +from autopilot.emulators.X11 import Keyboard +from autopilot.emulators.unity import Launcher, Switcher +from autopilot.tests import AutopilotTestCase +from autopilot.glibrunner import GlibRunner + + +class ShowDesktopTests(AutopilotTestCase): + """Test the 'Show Desktop' functionality.""" + run_test_with = GlibRunner + + def setUp(self): + super(ShowDesktopTests, self).setUp() + self.addCleanup(Keyboard.cleanup) + self.bamf = Bamf() + + def launch_test_apps(self): + """Launch character map and calculator apps.""" + self.bamf.launch_application("gucharmap.desktop") + self.addCleanup(call, ["killall", "gucharmap"]) + self.bamf.launch_application("gcalctool.desktop") + self.addCleanup(call, ["killall", "gcalctool"]) + + def test_showdesktop_hides_apps(self): + """Show Desktop keyboard shortcut must hide applications.""" + self.launch_test_apps() + + # show desktop, verify all windows are hidden: + kb = Keyboard() + kb.press_and_release('Control+Alt+d') + self.addCleanup(kb.press_and_release, keys='Control+Alt+d') + sleep(1) + open_wins = self.bamf.get_open_windows() + self.assertGreaterEqual(len(open_wins), 2) + for win in open_wins: + self.assertTrue(win.is_valid) + self.assertTrue(win.is_hidden, "Window '%s' is not hidden after show desktop activated." % (win.title)) + + def test_showdesktop_unhides_apps(self): + """Show desktop shortcut must re-show all hidden apps.""" + self.launch_test_apps() + + # show desktop, verify all windows are hidden: + kb = Keyboard() + kb.press_and_release('Control+Alt+d') + sleep(1) + open_wins = self.bamf.get_open_windows() + self.assertGreaterEqual(len(open_wins), 2) + for win in open_wins: + self.assertTrue(win.is_valid) + self.assertTrue(win.is_hidden, "Window '%s' is not hidden after show desktop activated." % (win.title)) + + # un-show desktop, verify all windows are shown: + kb.press_and_release('Control+Alt+d') + sleep(1) + for win in self.bamf.get_open_windows(): + self.assertTrue(win.is_valid) + self.assertFalse(win.is_hidden, "Window '%s' is shown after show desktop deactivated." % (win.title)) + + def test_unhide_single_app(self): + """Un-hide a single app from launcher after hiding all apps.""" + self.launch_test_apps() + + # show desktop, verify all windows are hidden: + kb = Keyboard() + kb.press_and_release('Control+Alt+d') + sleep(1) + open_wins = self.bamf.get_open_windows() + self.assertGreaterEqual(len(open_wins), 2) + for win in open_wins: + self.assertTrue(win.is_valid) + self.assertTrue(win.is_hidden, "Window '%s' is not hidden after show desktop activated." % (win.title)) + + # We'll un-minimise the character map - find it's launcherIcon in the launcher: + l = Launcher() + + launcher_icons = l.get_launcher_icons() + found = False + for icon in launcher_icons: + if icon.tooltip_text == 'Character Map': + found = True + l.click_launcher_icon(icon) + self.assertTrue(found, "Could not find launcher icon in launcher.") + + sleep(1) + for win in self.bamf.get_open_windows(): + if win.is_valid: + if win.title == 'Character Map': + self.assertFalse(win.is_hidden, "Character map did not un-hide from launcher.") + else: + self.assertTrue(win.is_hidden, "Window '%s' should still be hidden." % (win.title)) + + # hide desktop - now all windows should be visible: + kb.press_and_release('Control+Alt+d') + sleep(1) + for win in self.bamf.get_open_windows(): + if win.is_valid: + self.assertFalse(win.is_hidden, "Window '%s' is not shown after show desktop deactivated." % (win.title)) + + def test_showdesktop_switcher(self): + """Show desktop item in switcher should hide all hidden apps.""" + self.launch_test_apps() + + # show desktop, verify all windows are hidden: + switcher = Switcher() + switcher.initiate() + sleep(0.5) + found = False + for i in range(switcher.get_model_size()): + current_icon = switcher.current_icon + self.assertIsNotNone(current_icon) + if current_icon.tooltip_text == 'Show Desktop': + found = True + break + switcher.previous_icon() + sleep(0.5) + self.assertTrue(found, "Could not find 'Show Desktop' entry in switcher.") + switcher.terminate() + kb = Keyboard() + self.addCleanup(kb.press_and_release, keys='Control+Alt+d') + + sleep(1) + open_wins = self.bamf.get_open_windows() + self.assertGreaterEqual(len(open_wins), 2) + for win in open_wins: + self.assertTrue(win.is_valid) + self.assertTrue(win.is_hidden, "Window '%s' is not hidden after show desktop activated." % (win.title)) diff --git a/tests/autopilot/autopilot/tests/test_switcher.py b/tests/autopilot/autopilot/tests/test_switcher.py index f78f9d2c6..a4917bc99 100644 --- a/tests/autopilot/autopilot/tests/test_switcher.py +++ b/tests/autopilot/autopilot/tests/test_switcher.py @@ -1,8 +1,14 @@ -import gio +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2010 Canonical +# Author: Thomi Richards +# +# 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. + from subprocess import call from time import sleep -from testtools import TestCase from testtools.matchers import Equals from testtools.matchers import NotEquals @@ -11,32 +17,30 @@ from compizconfig import Plugin from autopilot.globals import global_context from autopilot.emulators.unity import Switcher +from autopilot.emulators.bamf import Bamf +from autopilot.tests import AutopilotTestCase from autopilot.glibrunner import GlibRunner - -class SwitcherTests(TestCase): +class SwitcherTests(AutopilotTestCase): + """Test the switcher.""" run_test_with = GlibRunner - def launch_application(self, desktop_file): - proc = gio.unix.DesktopAppInfo(desktop_file) - proc.launch() - def set_timeout_setting(self, value): self.setting.Value = value global_context.Write() - + def setUp(self): self.plugin = Plugin(global_context, "unityshell") self.setting = Setting(self.plugin, "alt_tab_timeout") + self.bamf = Bamf() + + self.bamf.launch_application("gucharmap.desktop") + self.bamf.launch_application("gcalctool.desktop") + self.bamf.launch_application("mahjongg.desktop") - self.launch_application("gucharmap.desktop") - self.launch_application("gcalctool.desktop") - self.launch_application("mahjongg.desktop") - - sleep(5) super(SwitcherTests, self).setUp() - + self.server = Switcher() def tearDown(self): @@ -52,32 +56,32 @@ class SwitcherTests(TestCase): self.server.initiate() sleep(.2) - + start = self.server.get_selection_index() self.server.next_icon() sleep(.2) - + end = self.server.get_selection_index() self.server.terminate() - + self.assertThat(start, NotEquals(0)) self.assertThat(end, Equals(start+1)) self.set_timeout_setting(True) - + def test_switcher_move_prev(self): self.set_timeout_setting(False) sleep(1) self.server.initiate() sleep(.2) - + start = self.server.get_selection_index() self.server.previous_icon() sleep(.2) - + end = self.server.get_selection_index() self.server.terminate() - + self.assertThat(start, NotEquals(0)) self.assertThat(end, Equals(start-1)) self.set_timeout_setting(True) diff --git a/tests/test_hud.cpp b/tests/test_hud.cpp new file mode 100644 index 000000000..36f2c876b --- /dev/null +++ b/tests/test_hud.cpp @@ -0,0 +1,104 @@ +#include <gtest/gtest.h> +#include <glib-object.h> +#include <UnityCore/GLibWrapper.h> +#include <UnityCore/Hud.h> + +using namespace std; + +namespace +{ + +GMainLoop* loop_ = NULL; +unity::hud::Hud *hud; + +class TestHud : public ::testing::Test +{ +public: + TestHud() + : query_return_result(false) + , connected_result(false) + { + } + unity::hud::Hud::Queries queries; + bool query_return_result; + bool connected_result; +}; + +TEST_F(TestHud, TestConstruction) +{ + loop_ = g_main_loop_new(NULL, FALSE); + hud = new unity::hud::Hud("com.canonical.Unity.Test", "/com/canonical/hud"); + + // performs a check on the hud, if the hud is connected, report a sucess + auto timeout_check = [] (gpointer data) -> gboolean + { + TestHud* self = static_cast<TestHud*>(data); + if (hud->connected) + { + self->connected_result = true; + g_main_loop_quit(loop_); + return FALSE; + } + else + { + self->connected_result = false; + return TRUE; + } + }; + + + // if the hud is not connected when this lambda runs, fail. + auto timeout_bailout = [] (gpointer data) -> gboolean + { + TestHud* self = static_cast<TestHud*>(data); + // reached timeout, failed testing + self->connected_result = false; + g_main_loop_quit(loop_); + return FALSE; + }; + + g_timeout_add_seconds(1, timeout_check, this); + g_timeout_add_seconds(10, timeout_bailout, this); + + g_main_loop_run(loop_); + + EXPECT_EQ(connected_result, true); +} + +TEST_F(TestHud, TestQueryReturn) +{ + query_return_result = false; + + // make sure we receive the queries + auto query_connection = [this](unity::hud::Hud::Queries queries_) + { + query_return_result = true; + g_main_loop_quit(loop_); + queries = queries_; + }; + + auto timeout_bailout = [] (gpointer data) -> gboolean + { + TestHud* self = static_cast<TestHud*>(data); + self->query_return_result = false; + g_main_loop_quit(loop_); + return FALSE; + }; + + hud->queries_updated.connect(query_connection); + + guint source_id = g_timeout_add_seconds(10, timeout_bailout, this); + + // next check we get 30 entries from this specific known callback + hud->RequestQuery("Request30Queries"); + g_main_loop_run(loop_); + EXPECT_EQ(query_return_result, true); + EXPECT_NE(queries.size(), 0); + g_source_remove(source_id); + + // finally close the connection - Nothing to check for here + hud->CloseQuery(); +} + + +} diff --git a/tests/test_service_hud.c b/tests/test_service_hud.c new file mode 100644 index 000000000..bfdf47ce1 --- /dev/null +++ b/tests/test_service_hud.c @@ -0,0 +1,268 @@ +#include "test_service_hud.h" +#include <unity.h> +#include <gio/gio.h> + +const char * hud_interface = +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +"<node name=\"/\">\n" +" <interface name=\"com.canonical.hud\">\n" +"<!-- Properties -->\n" +" <!-- None -->\n" +"\n" +"<!-- Functions -->\n" +" <method name=\"StartQuery\">\n" +" <!-- in -->\n" +" <arg type=\"s\" name=\"query\" direction=\"in\" />\n" +" <arg type=\"i\" name=\"entries\" direction=\"in\" />\n" +" <!-- out -->\n" +" <arg type=\"s\" name=\"target\" direction=\"out\" />\n" +" <arg type=\"a(sssssv)\" name=\"suggestions\" direction=\"out\" />\n" +" <arg type=\"v\" name=\"querykey\" direction=\"out\" />\n" +" </method>\n" +"\n" +" <method name=\"ExecuteQuery\">\n" +" <arg type=\"v\" name=\"key\" direction=\"in\" />\n" +" <arg type=\"u\" name=\"timestamp\" direction=\"in\" />\n" +" </method>\n" +"\n" +" <method name=\"CloseQuery\">\n" +" <arg type=\"v\" name=\"querykey\" direction=\"in\" />\n" +" </method>\n" +"\n" +"<!-- Signals -->\n" +" <signal name=\"UpdatedQuery\">\n" +" <arg type=\"s\" name=\"target\" direction=\"out\" />\n" +" <arg type=\"a(sssssv)\" name=\"suggestions\" direction=\"out\" />\n" +" <arg type=\"v\" name=\"querykey\" direction=\"out\" />\n" +" </signal>\n" +"\n" +"<!-- End of interesting stuff -->\n" +"\n" +" </interface>\n" +"</node>\n" +; +static void bus_got_cb (GObject *object, GAsyncResult * res, gpointer user_data); +static void bus_method (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data); + +G_DEFINE_TYPE(ServiceHud, service_hud, G_TYPE_OBJECT); +static GDBusNodeInfo * node_info = NULL; +static GDBusInterfaceInfo * iface_info = NULL; +static GDBusInterfaceVTable bus_vtable = { + method_call: bus_method, + get_property: NULL, + set_property: NULL, +}; + + +struct _ServiceHudPrivate +{ + GDBusConnection * bus; + GCancellable * bus_lookup; + guint bus_registration; +}; + +static void +service_hud_dispose(GObject* object) +{ + ServiceHud* self = SERVICE_HUD(object); + if (self->priv->bus_lookup != NULL) { + g_cancellable_cancel(self->priv->bus_lookup); + g_object_unref(self->priv->bus_lookup); + self->priv->bus_lookup = NULL; + } + + if (self->priv->bus_registration != 0) { + g_dbus_connection_unregister_object(self->priv->bus, self->priv->bus_registration); + self->priv->bus_registration = 0; + } + + if (self->priv->bus != NULL) { + g_object_unref(self->priv->bus); + self->priv->bus = NULL; + } + +} + +static void +service_hud_class_init(ServiceHudClass* klass) +{ + G_OBJECT_CLASS(klass)->dispose = service_hud_dispose; + g_type_class_add_private (klass, sizeof (ServiceHudPrivate)); + + if (node_info == NULL) + { + GError * error = NULL; + + node_info = g_dbus_node_info_new_for_xml(hud_interface, &error); + if (error != NULL) + { + g_error("Unable to parse HUD interface: %s", error->message); + g_error_free(error); + } + } + + if (node_info != NULL && iface_info == NULL) + { + iface_info = g_dbus_node_info_lookup_interface(node_info,"com.canonical.hud"); + if (iface_info == NULL) + { + g_error("Unable to find interface 'com.canonical.hud'"); + } + } + +} + +static void +service_hud_init(ServiceHud* self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, SERVICE_TYPE_HUD, ServiceHudPrivate); + self->priv->bus = NULL; + self->priv->bus_lookup = NULL; + self->priv->bus_registration = 0; + + self->priv->bus_lookup = g_cancellable_new(); + g_bus_get(G_BUS_TYPE_SESSION, self->priv->bus_lookup, bus_got_cb, self); + +} + +ServiceHud* +service_hud_new() +{ + return g_object_new(SERVICE_TYPE_HUD, NULL); +} + +static void +bus_got_cb (GObject *object, GAsyncResult * res, gpointer user_data) +{ + GError * error = NULL; + ServiceHud * self = SERVICE_HUD(user_data); + GDBusConnection * bus; + + bus = g_bus_get_finish(res, &error); + if (error != NULL) { + g_critical("Unable to get bus: %s", error->message); + g_error_free(error); + return; + } + + self->priv->bus = bus; + + /* Register object */ + self->priv->bus_registration = g_dbus_connection_register_object(bus, + /* path */ "/com/canonical/hud", + /* interface */ iface_info, + /* vtable */ &bus_vtable, + /* userdata */ self, + /* destroy */ NULL, + /* error */ &error); + + if (error != NULL) { + g_critical ("Unable to create bus connection object, %s", error->message); + g_error_free(error); + return; + } + + return; +} + +static void +bus_method (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) +{ + if (g_strcmp0(method_name, "StartQuery") == 0) + { + GVariant * ret = NULL; + gchar * query = NULL; + int num_entries = 0; + + g_variant_get(parameters, "(si)", &query, &num_entries); + + /* Build into into a variant */ + GVariantBuilder ret_builder; + g_variant_builder_init(&ret_builder, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&ret_builder, g_variant_new_string("target")); + GVariantBuilder builder; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + + int i = 0; + for (i = 0; i < num_entries; i++) + { + gchar* target = g_strdup_printf("test-%i", i); + gchar* icon = g_strdup_printf("icon-%i", i); + gchar* future_icon = g_strdup(icon); + gchar* completion_text = g_strdup_printf("completion-%i", i); + gchar* accelerator = g_strdup_printf("<alt>+whatever"); + + GVariantBuilder tuple; + g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&tuple, g_variant_new_string(target)); + g_variant_builder_add_value(&tuple, g_variant_new_string(icon)); + g_variant_builder_add_value(&tuple, g_variant_new_string(future_icon)); + g_variant_builder_add_value(&tuple, g_variant_new_string(completion_text)); + g_variant_builder_add_value(&tuple, g_variant_new_string(accelerator)); + // build a fake key + GVariant* key; + { + GVariantBuilder keybuilder; + g_variant_builder_init(&keybuilder, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); + g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); + g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); + g_variant_builder_add_value(&keybuilder, g_variant_new_int32(1986)); + + key = g_variant_new_variant(g_variant_builder_end(&keybuilder)); + } + g_variant_ref_sink(key); + g_variant_builder_add_value(&tuple, key); + g_variant_builder_add_value(&builder, g_variant_builder_end(&tuple)); + g_free(target); + g_free(icon); + g_free(future_icon); + g_free(completion_text); + } + g_variant_builder_add_value(&ret_builder, g_variant_builder_end(&builder)); + + GVariant* query_key; + { + GVariantBuilder keybuilder; + g_variant_builder_init(&keybuilder, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); + g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); + g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); + g_variant_builder_add_value(&keybuilder, g_variant_new_int32(1986)); + + query_key = g_variant_new_variant(g_variant_builder_end(&keybuilder)); + } + g_variant_ref_sink(query_key); + g_variant_builder_add_value(&ret_builder, query_key); + + ret = g_variant_builder_end(&ret_builder); + + g_dbus_method_invocation_return_value(invocation, ret); + g_free(query); + } + else if (g_strcmp0(method_name, "ExecuteQuery") == 0) + { + GVariant * key = NULL; + key = g_variant_get_child_value(parameters, 0); + g_dbus_method_invocation_return_value(invocation, NULL); + g_variant_unref(key); + } + else if (g_strcmp0(method_name, "CloseQuery") == 0) + { + GVariant * key = NULL; + key = g_variant_get_child_value(parameters, 0); + g_dbus_method_invocation_return_value(invocation, NULL); + g_variant_unref(key); + } + + return; +} + diff --git a/tests/test_service_hud.h b/tests/test_service_hud.h new file mode 100644 index 000000000..df069962a --- /dev/null +++ b/tests/test_service_hud.h @@ -0,0 +1,46 @@ +#ifndef _SERVICE_HUD_H_ +#define _SERVICE_HUD_H_ + +#include <glib-object.h> +G_BEGIN_DECLS + +#define SERVICE_TYPE_HUD (service_hud_get_type ()) + +#define SERVICE_HUD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + SERVICE_TYPE_HUD, ServiceHud)) + +#define SERVICE_HUD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + SERVICE_TYPE_HUD, ServiceHudClass)) + +#define SERVICE_IS_HUD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + SERVICE_TYPE_HUD)) + +#define SERVICE_IS_HUD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + SERVICE_TYPE_HUD)) + +#define ServiceHud_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + SERVICE_TYPE_HUD, ServiceHudClass)) + +typedef struct _ServiceHud ServiceHud; +typedef struct _ServiceHudClass ServiceHudClass; +typedef struct _ServiceHudPrivate ServiceHudPrivate; + +struct _ServiceHud +{ + GObject parent; + + ServiceHudPrivate *priv; +}; + +struct _ServiceHudClass +{ + GObjectClass parent_class; +}; + +GType service_hud_get_type(void) G_GNUC_CONST; + +ServiceHud* service_hud_new(void); + +G_END_DECLS + +#endif /* _SERVICE_HUD_H_ */ diff --git a/tests/test_service_main.c b/tests/test_service_main.c index 7e2bdc15c..e03bef41c 100644 --- a/tests/test_service_main.c +++ b/tests/test_service_main.c @@ -2,6 +2,7 @@ #include "test_service_lens.h" #include "test_service_model.h" +#include "test_service_hud.h" static void on_bus_aquired(GDBusConnection* conn, const gchar* name, gpointer null); static void handle_method_call(GDBusConnection *connection, @@ -34,6 +35,7 @@ static const GDBusInterfaceVTable interface_vtable = static GMainLoop* loop_ = NULL; static ServiceLens* lens_ = NULL; static ServiceModel* model_ = NULL; +static ServiceHud* hud_ = NULL; gint main(gint argc, gchar** argv) @@ -44,6 +46,7 @@ main(gint argc, gchar** argv) lens_ = service_lens_new(); model_ = service_model_new(); + hud_ = service_hud_new(); g_bus_own_name(G_BUS_TYPE_SESSION, "com.canonical.Unity.Test", @@ -57,8 +60,9 @@ main(gint argc, gchar** argv) g_main_loop_run(loop_); g_main_loop_unref(loop_); - g_object_unref(lens_); - g_object_unref(model_); + //g_object_unref(lens_); + //g_object_unref(model_); + g_object_unref(hud_); g_dbus_node_info_unref(introspection_data); return 0; diff --git a/tests/unit/TestQuicklistMenuitems.cpp b/tests/unit/TestQuicklistMenuitems.cpp index 823715f8e..716e0dc03 100644 --- a/tests/unit/TestQuicklistMenuitems.cpp +++ b/tests/unit/TestQuicklistMenuitems.cpp @@ -241,6 +241,7 @@ TestQuicklistMenuItem() g_assert_cmpstr(quicklist->GetNthItems(2)->GetLabel(), == , "label 1"); g_assert_cmpstr(quicklist->GetNthItems(3)->GetLabel(), == , "check mark 0"); + g_assert_cmpint(quicklist->GetChildren().size(), == , 4); quicklist->Dispose(); } diff --git a/tools/unity-introspection-visualiser.py b/tools/unity-introspection-visualiser.py index fae236e2c..bdefe0b51 100755 --- a/tools/unity-introspection-visualiser.py +++ b/tools/unity-introspection-visualiser.py @@ -2,7 +2,9 @@ # # Script to generate a nice PNG file of the currently running unity introspection tree. -from sys import argv +from argparse import ArgumentParser +from os import remove +from os.path import splitext import dbus try: @@ -21,6 +23,7 @@ except ImportError: NEXT_NODE_ID=1 + def string_rep(dbus_type): """Get a string representation of various dbus types.""" if type(dbus_type) == dbus.Boolean: @@ -60,16 +63,17 @@ def traverse_tree(state, parent, graph): graph.add_edge(pydot.Edge(parent, child)) traverse_tree(child_state, child, graph) - + if __name__ == '__main__': - if len(argv) != 2: - print """Usage: %s output_file.png. + parser = ArgumentParser() + mg = parser.add_mutually_exclusive_group(required=True) + mg.add_argument('-o', '--output', nargs=1, + help='Store output in specified file on disk.') + mg.add_argument('-d','--display', action='store_true', + help='Display output in image viewer.') -This script queries the currently running Unity process and dumps the entire -introspection tree into a graph, and renders this to a PNG file. -""" % (argv[0]) - exit(1) + args = parser.parse_args() u = Unity() introspection_tree = u.get_state() @@ -78,10 +82,27 @@ introspection tree into a graph, and renders this to a PNG file. graph.set_node_defaults(shape='Mrecord') graph.set_fontname('Ubuntu') graph.set_fontsize('10') - + gnode_unity = pydot.Node("Unity") gnode_unity.set_comment("Unity") traverse_tree(introspection_tree[0], gnode_unity, graph) - graph.write(argv[1], format='png') + if args.output: + base, extension = splitext(args.output[0]) + write_method_name = 'write_' + extension[1:] + if hasattr(graph, write_method_name): + getattr(graph, write_method_name)(args.output[0]) + else: + print "Error: unsupported format: '%s'" % (extension) + elif args.display: + from tempfile import NamedTemporaryFile + from subprocess import call + tf = NamedTemporaryFile(suffix='.png', delete=False) + tf.write(graph.create_png()) + tf.close() + call(["eog", tf.name]) + remove(tf.name) + else: + print 'unknown output mode!' + |
