python-efl/efl/elementary/theme.pyx

487 lines
18 KiB
Cython

# Copyright (C) 2007-2013 various contributors (see AUTHORS)
#
# This file is part of Python-EFL.
#
# Python-EFL is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3 of the License, or (at your option) any later version.
#
# Python-EFL 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this Python-EFL. If not, see <http://www.gnu.org/licenses/>.
"""
Widget description
------------------
Elementary uses Edje to theme its widgets, naturally. But for the most
part this is hidden behind a simpler interface that lets the user set
extensions and choose the style of widgets in a much easier way.
Instead of thinking in terms of paths to Edje files and their groups
each time you want to change the appearance of a widget, Elementary
works so you can add any theme file with extensions or replace the
main theme at one point in the application, and then just set the style
of widgets with
:py:attr:`Object.style<efl.elementary.object.Object.style>`
and related functions. Elementary
will then look in its list of themes for a matching group and apply it,
and when the theme changes midway through the application, all widgets
will be updated accordingly.
There are three concepts you need to know to understand how Elementary
theming works: default theme, extensions and overlays.
Default theme, obviously enough, is the one that provides the default
look of all widgets. End users can change the theme used by Elementary
by setting the ``ELM_THEME`` environment variable before running an
application, or globally for all programs using the ``elementary_config``
utility. Applications can change the default theme using :py:attr:`Theme.order`,
but this can go against the user wishes, so it's not an advised practice.
Ideally, applications should find everything they need in the already
provided theme, but there may be occasions when that's not enough and
custom styles are required to correctly express the idea. For this
cases, Elementary has extensions.
Extensions allow the application developer to write styles of its own
to apply to some widgets. This requires knowledge of how each widget
is themed, as extensions will always replace the entire group used by
the widget, so important signals and parts need to be there for the
object to behave properly (see documentation of Edje for details).
Once the theme for the extension is done, the application needs to add
it to the list of themes Elementary will look into, using
:py:func:`Theme.extension_add()`, and set the style of the desired widgets as
he would normally with
:py:attr:`Object.style<efl.elementary.object.Object.style>`.
Overlays, on the other hand, can replace the look of all widgets by
overriding the default style. Like extensions, it's up to the application
developer to write the theme for the widgets it wants, the difference
being that when looking for the theme, Elementary will check first the
list of overlays, then the set theme and lastly the list of extensions,
so with overlays it's possible to replace the default view and every
widget will be affected. This is very much alike to setting the whole
theme for the application and will probably clash with the end user
options, not to mention the risk of ending up with not matching styles
across the program. Unless there's a very special reason to use them,
overlays should be avoided for the reasons exposed before.
All these theme lists are handled by :py:class:`Theme` instances. Elementary
keeps one default internally. It's possible to create a new instance of a
:py:class:`Theme` to set other theme for a specific widget (and all of its
children), but this is as discouraged, if not even more so, than using
overlays. Don't use this unless you really know what you are doing.
.. note::
Remember to :py:func:`Theme.free` the instance when you're done with it!
"""
from cpython cimport PyUnicode_AsUTF8String, Py_INCREF, Py_DECREF
from efl.eo cimport PY_REFCOUNT
from efl.utils.conversions cimport _ctouni, eina_list_strings_to_python_list
cdef class Theme(object):
"""
This is the class that actually implements the widget.
"""
def __repr__(self):
return "<%s object at %#x (refcount=%d, order=%s, overlay_list=%s, extension_list=%s)>" % (
type(self).__name__,
<unsigned long>self.th,
PY_REFCOUNT(self),
_ctouni(elm_theme_get(self.th)),
eina_list_strings_to_python_list(elm_theme_overlay_list_get(self.th)),
eina_list_strings_to_python_list(elm_theme_extension_list_get(self.th))
)
def __init__(self, default=False):
self.th = elm_theme_new() if not default else elm_theme_default_get()
if self.th == NULL:
raise RuntimeError
@classmethod
def new(type cls):
"""Create a new specific theme
This creates an empty specific theme that only uses the default
theme. A specific theme has its own private set of extensions and
overlays too (which are empty by default). Specific themes do not
fall back to themes of parent objects. They are not intended for
this use. Use styles, overlays and extensions when needed, but avoid
specific themes unless there is no other way (example: you want to
have a preview of a new theme you are selecting in a "theme
selector" window. The preview is inside a scroller and should
display what the theme you selected will look like, but not actually
apply it yet. The child of the scroller will have a specific theme
set to show this preview before the user decides to apply it to all
applications).
"""
cdef Theme ret = cls.__new__(cls)
ret.th = elm_theme_new()
if ret.th == NULL:
raise RuntimeError
else:
return ret
@classmethod
def default_get(type cls):
"""Return the default theme
This returns the internal default theme setup handle that all widgets
use implicitly unless a specific theme is set. This is also often use
as a shorthand of NULL.
"""
cdef Theme ret = cls.__new__(cls)
ret.th = elm_theme_default_get()
if ret.th == NULL:
raise RuntimeError
else:
return ret
def free(self):
"""free()
Free the theme.
"""
if self.th != NULL:
elm_theme_free(self.th)
self.th = NULL
def copy(self, Theme dst not None):
"""copy(Theme dst)
Copy the theme from the source to the destination theme
This makes a one-time static copy of all the theme config, extensions
and overlays from ``th`` to ``thdst``. If ``th`` references a theme, then
``thdst`` is also set to reference it, with all the theme settings,
overlays and extensions that ``th`` had.
:param Theme thdst: The destination theme to copy data to
"""
elm_theme_copy(self.th, dst.th)
property reference:
"""Theme reference
Setting this clears ``th`` to be empty and then sets it to refer to
``thref`` so ``th`` acts as an override to ``thref``, but where its
overrides don't apply, it will fall through to ``thref`` for
configuration.
Getting it returns the theme that is referred to.
:type: :py:class:`Theme`
"""
def __set__(self, Theme thref not None):
elm_theme_ref_set(self.th, thref.th)
def __get__(self):
cdef Theme ret = Theme.__new__(Theme)
ret.th = elm_theme_ref_get(self.th)
if ret.th == NULL:
return None
else:
return ret
def ref_set(self, Theme thref not None):
elm_theme_ref_set(self.th, thref.th)
def ref_get(self):
cdef Theme ret = Theme.__new__(Theme)
ret.th = elm_theme_ref_get(self.th)
if ret.th == NULL:
return None
else:
return ret
def overlay_add(self, item not None):
"""overlay_add(unicode item)
Prepends a theme overlay to the list of overlays
Use this if your application needs to provide some custom overlay
theme (An Edje file that replaces some default styles of widgets)
where adding new styles, or changing system theme configuration is
not possible. Do NOT use this instead of a proper system theme
configuration. Use proper configuration files, profiles, environment
variables etc. to set a theme so that the theme can be altered by
simple configuration by a user. Using this call to achieve that
effect is abusing the API and will create lots of trouble.
.. seealso:: :py:func:`extension_add()`
:param string item: The Edje file path to be used
"""
if isinstance(item, unicode): item = PyUnicode_AsUTF8String(item)
elm_theme_overlay_add(self.th,
<const_char *>item)
def overlay_del(self, item not None):
"""overlay_del(unicode item)
Delete a theme overlay from the list of overlays
.. seealso:: :py:func:`overlay_add()`
:param item: The name of the theme overlay
:type item: string
"""
if isinstance(item, unicode): item = PyUnicode_AsUTF8String(item)
elm_theme_overlay_del(self.th,
<const_char *>item)
property overlay_list:
"""Get the list of registered overlays for the given theme
.. seealso:: :py:func:`overlay_add()`
:type: tuple of strings
"""
def __get__(self):
return tuple(eina_list_strings_to_python_list(elm_theme_overlay_list_get(self.th)))
def overlay_list_get(self):
return tuple(eina_list_strings_to_python_list(elm_theme_overlay_list_get(self.th)))
def extension_add(self, item not None):
"""extension_add(unicode item)
Appends a theme extension to the list of extensions.
This is intended when an application needs more styles of widgets or
new widget themes that the default does not provide (or may not
provide). The application has "extended" usage by coming up with new
custom style names for widgets for specific uses, but as these are
not "standard", they are not guaranteed to be provided by a default
theme. This means the application is required to provide these extra
elements itself in specific Edje files. This call adds one of those
Edje files to the theme search path to be search after the default
theme. The use of this call is encouraged when default styles do not
meet the needs of the application. Use this call instead of
elm_theme_overlay_add() for almost all cases.
.. seealso:: :py:attr:`Object.style<efl.elementary.object.Object.style>`
:param item: The Edje file path to be used
:type item: string
"""
if isinstance(item, unicode): item = PyUnicode_AsUTF8String(item)
elm_theme_extension_add(self.th,
<const_char *>item)
def extension_del(self, item not None):
"""extension_del(unicode item)
Deletes a theme extension from the list of extensions.
.. seealso:: :py:func:`extension_add()`
:param item: The name of the theme extension
:type item: string
"""
if isinstance(item, unicode): item = PyUnicode_AsUTF8String(item)
elm_theme_extension_del(self.th,
<const_char *>item)
property extension_list:
"""Get the list of registered extensions for the given theme
.. seealso:: :py:func:`extension_add()`
:type: tuple of strings
"""
def __get__(self):
return tuple(eina_list_strings_to_python_list(elm_theme_extension_list_get(self.th)))
def extension_list_get(self):
return tuple(eina_list_strings_to_python_list(elm_theme_extension_list_get(self.th)))
property order:
"""Set the theme search order for the given theme
This sets the search string for the theme in path-notation from first
theme to search, to last, delimited by the : character. Example:
"shiny:/path/to/file.edj:default"
See the ELM_THEME environment variable for more information.
.. seealso:: :py:attr:`elements`
:type: string
"""
def __set__(self, theme not None):
if isinstance(theme, unicode): theme = PyUnicode_AsUTF8String(theme)
elm_theme_set(self.th,
<const_char *>theme if theme is not None else NULL)
def __get__(self):
return _ctouni(elm_theme_get(self.th))
def order_set(self, theme not None):
if isinstance(theme, unicode): theme = PyUnicode_AsUTF8String(theme)
elm_theme_set(self.th,
<const_char *>theme if theme is not None else NULL)
def order_get(self):
return _ctouni(elm_theme_get(self.th))
property elements:
"""Return a list of theme elements to be used in a theme.
This returns the internal list of theme elements (will only be valid as
long as the theme is not modified by elm_theme_set() or theme is not
freed by elm_theme_free(). This is a list of strings which must not be
altered as they are also internal. If ``th`` is NULL, then the default
theme element list is returned.
A theme element can consist of a full or relative path to a .edj file,
or a name, without extension, for a theme to be searched in the known
theme paths for Elementary.
.. seealso:: :py:attr:`order`
:type: tuple of strings
"""
def __get__(self):
return tuple(eina_list_strings_to_python_list(elm_theme_list_get(self.th)))
def elements_get(self):
return tuple(eina_list_strings_to_python_list(elm_theme_list_get(self.th)))
def flush(self):
"""flush()
Flush the current theme.
This flushes caches that let elementary know where to find theme elements
in the given theme. If ``th`` is NULL, then the default theme is flushed.
Call this function if source theme data has changed in such a way as to
make any caches Elementary kept invalid.
"""
elm_theme_flush(self.th)
def data_get(self, key not None):
"""data_get(unicode key) -> unicode
Get a data item from a theme
This function is used to return data items from edc in ``th``, an
overlay, or an extension. It works the same way as
edje_file_data_get() except that the return is stringshared.
:param key: The data key to search with
:type key: string
:return: The data value, or NULL on failure
:rtype: string
"""
if isinstance(key, unicode): key = PyUnicode_AsUTF8String(key)
return _ctouni(elm_theme_data_get(self.th,
<const_char *>key if key is not None else NULL))
def theme_list_item_path_get(f not None, bint in_search_path):
"""theme_list_item_path_get(unicode f, bool in_search_path) -> unicode
Return the full path for a theme element
This returns a string you should free with free() on success, NULL on
failure. This will search for the given theme element, and if it is a
full or relative path element or a simple search-able name. The returned
path is the full path to the file, if searched, and the file exists, or it
is simply the full path given in the element or a resolved path if
relative to home. The ``in_search_path`` boolean pointed to is set to
EINA_TRUE if the file was a search-able file and is in the search path,
and EINA_FALSE otherwise.
:param string f: The theme element name
:param bool in_search_path: Pointer to a boolean to indicate if item is in the search path or not
:return: The full path to the file found.
:rtype: string
"""
cdef Eina_Bool path = in_search_path
if isinstance(f, unicode): f = PyUnicode_AsUTF8String(f)
return _ctouni(elm_theme_list_item_path_get(
<const_char *>f if f is not None else NULL, &path))
def theme_full_flush():
"""theme_full_flush()
This flushes all themes (default and specific ones).
This will flush all themes in the current application context, by calling
elm_theme_flush() on each of them.
"""
elm_theme_full_flush()
def theme_name_available_list():
"""theme_name_available_list()
Return a list of theme elements in the theme search path
This lists all available theme files in the standard Elementary search path
for theme elements, and returns them in alphabetical order as theme
element names in a list of strings.
:return: A list of strings that are the theme element names.
:rtype: tuple of strings
"""
cdef Eina_List *lst = elm_theme_name_available_list_new()
elements = tuple(eina_list_strings_to_python_list(lst))
elm_theme_name_available_list_free(lst)
return elements
# for compatibility
def theme_overlay_add(item not None):
if isinstance(item, unicode): item = PyUnicode_AsUTF8String(item)
elm_theme_overlay_add(NULL,
<const_char *>item)
def theme_extension_add(item not None):
if isinstance(item, unicode): item = PyUnicode_AsUTF8String(item)
elm_theme_extension_add(NULL,
<const_char *>item)