Add color as type of icon and image

Closes #13808
This commit is contained in:
Cédric Krier
2025-01-09 18:52:36 +01:00
parent ff6f920521
commit 203a5f3a34
14 changed files with 80 additions and 13 deletions
+1
View File
@@ -1,3 +1,4 @@
* Support color as type of icon and image
* Support color widget on form view
Version 7.4.0 - 2024-11-04
+5
View File
@@ -1254,6 +1254,11 @@ function eval_pyson(value){
} else {
this.img.removeAttr('src');
}
} else if (this.attributes.type == 'color') {
this.img.attr('src', Sao.common.image_url());
// clean previous color if the new one is not valid
this.img.css('background-color', '');
this.img.css('background-color', name);
} else {
Sao.common.ICONFACTORY.get_icon_url(name)
.done(url => {
+6
View File
@@ -2312,6 +2312,12 @@
} else {
img_tag.removeAttr('src');
}
} else if (this.attributes.icon_type == 'color') {
img_tag.attr('src', Sao.common.image_url());
// clean previous color if the new one is not valid
img_tag.css('background-color', '');
img_tag.css('background-color', value);
img_tag.toggle(Boolean(value));
} else {
Sao.common.ICONFACTORY.get_icon_url(value)
.done(url => {
+1
View File
@@ -1,3 +1,4 @@
* Support color as type of icon and image
* Support color widget on form view
Version 7.4.0 - 2024-11-04
+11 -9
View File
@@ -4,15 +4,16 @@ from . import timedelta
from .common import (
COLOR_SCHEMES, MODELACCESS, MODELHISTORY, MODELNAME, MODELNOTIFICATION,
TRYTON_ICON, VIEW_SEARCH, IconFactory, Logout, RPCContextReload,
RPCException, RPCExecute, RPCProgress, Tooltips, apply_label_attributes,
ask, check_version, concurrency, data2pixbuf, date_format, ellipsize,
error, file_open, file_selection, file_write, filter_domain,
generateColorscheme, get_align, get_credentials, get_gdk_backend,
get_hostname, get_port, get_sensible_widget, get_toplevel_window, hex2rgb,
highlight_rgb, humanize, idle_add, mailto, message, node_attributes,
open_documentation, play_sound, process_exception, resize_pixbuf,
selection, setup_window, slugify, sur, sur_3b, timezoned_date, to_xml,
untimezoned_date, url_open, userwarning, warning)
RPCException, RPCExecute, RPCProgress, SolidColorFactory, Tooltips,
apply_label_attributes, ask, check_version, concurrency, data2pixbuf,
date_format, ellipsize, error, file_open, file_selection, file_write,
filter_domain, generateColorscheme, get_align, get_credentials,
get_gdk_backend, get_hostname, get_port, get_sensible_widget,
get_toplevel_window, hex2rgb, highlight_rgb, humanize, idle_add, mailto,
message, node_attributes, open_documentation, play_sound,
process_exception, resize_pixbuf, selection, setup_window, slugify, sur,
sur_3b, timezoned_date, to_xml, untimezoned_date, url_open, userwarning,
warning)
from .domain_inversion import (
concat, domain_inversion, eval_domain, extract_reference_models,
filter_leaf, inverse_leaf, localize_domain, merge,
@@ -32,6 +33,7 @@ __all__ = [
RPCException,
RPCExecute,
RPCProgress,
SolidColorFactory,
TRYTON_ICON,
Tooltips,
VIEW_SEARCH,
+43
View File
@@ -233,6 +233,49 @@ class IconFactory:
IconFactory.load_local_icons()
class SolidColorFactory:
_pixbufs = defaultdict(dict)
@staticmethod
def _create_pixbuf(red, green, blue, width, height):
pixbuf = GdkPixbuf.Pixbuf.new(
GdkPixbuf.Colorspace.RGB, False, 8, width, height)
pixels = bytearray(pixbuf.get_pixels())
rowstride = pixbuf.get_rowstride()
for y in range(height):
for x in range(width):
offset = y * rowstride + x * 3
pixels[offset:offset + 3] = bytes([red, green, blue])
return GdkPixbuf.Pixbuf.new_from_data(
pixels, GdkPixbuf.Colorspace.RGB, False, 8, width, height,
rowstride)
@staticmethod
def hex_to_rgb(color):
color = color.lstrip('#')
if len(color) == 3:
color = ''.join([c * 2 for c in color])
if len(color) != 6:
return
try:
red = int(color[0:2], 16)
green = int(color[2:4], 16)
blue = int(color[4:6], 16)
except ValueError:
return
return red, green, blue
@classmethod
def get_pixbuf(cls, color, width=16, height=16):
rgb = cls.hex_to_rgb(color)
if rgb is None:
return
if rgb not in cls._pixbufs[width, height]:
cls._pixbufs[width, height][rgb] = cls._create_pixbuf(
*rgb, width, height)
return cls._pixbufs[width, height][rgb]
class ModelAccess(object):
batchnum = 100
@@ -72,6 +72,8 @@ class Image(StateMixin, Gtk.Image):
if self.attrs.get('type') == 'url':
pixbuf = common.IconFactory.get_pixbuf_url(
name, size=size, size_param=self.attrs.get('url_size'))
elif self.attrs.get('type') == 'color':
pixbuf = common.SolidColorFactory.get_pixbuf(name, size, size)
else:
pixbuf = common.IconFactory.get_pixbuf(name, size)
self.set_from_pixbuf(pixbuf)
@@ -221,6 +221,8 @@ class Affix(Cell):
pixbuf = common.IconFactory.get_pixbuf_url(
value, size_param=self.attrs.get('url_size'),
callback=callback)
elif self.attrs.get('icon_type') == 'color':
pixbuf = common.SolidColorFactory.get_pixbuf(value)
else:
pixbuf = common.IconFactory.get_pixbuf(
value, Gtk.IconSize.BUTTON)
+1
View File
@@ -1,3 +1,4 @@
* Add color as type of icon and image
* Add color widget to form view
* Limit access to database from the same series
* Fill wizard state view instance with default value
+4 -2
View File
@@ -392,7 +392,7 @@ image
Display an image with those attributes:
``type``
The type of image source. Available values are ``icon`` or ``url``.
The type of image source. Available values are ``icon``, ``url`` or ``color``.
The default value is ``icon``.
``name``
@@ -400,6 +400,7 @@ Display an image with those attributes:
For the ``icon`` type it must be the name of an `Icon <model-ir.ui.icon>`
record.
For the ``url`` type it must be the URL. It can be relative to the server.
For the ``color`` type it must be the color in ``#rrggbb`` hexadecimal.
``url_size``
The name of the size parameter to add to the URL.
@@ -754,9 +755,10 @@ displayed in the same column with the attributes:
For the ``icon`` type it must be the name of an `Icon <model-ir.ui.icon>`
record.
For the ``url`` type it must be the URL and it can be relative to the server.
For the ``color`` type it must be the color in ``#rrggbb`` hexadecimal.
``icon_type``
The type of icon source. Available values are ``icon`` or ``url``.
The type of icon source. Available values are ``icon``, ``url`` or ``color``.
The default value is ``icon``.
``url_size``
+1 -1
View File
@@ -125,7 +125,7 @@ attlist.field &= [a:defaultValue = "square"] attribute border { "square" | "circ
attlist.field &= [a:defaultValue = "0"] attribute visible { "0" | "1" }?
image = element image { attlist.image, empty }
attlist.image &= attribute name { text }
attlist.image &= [a:defaultValue = "icon"] attribute type { "icon" | "url" }?
attlist.image &= [a:defaultValue = "icon"] attribute type { "icon" | "url" | "color" }?
attlist.image &= attribute url_size { text }?
attlist.image &= [a:defaultValue = "square"] attribute border { "square" | "circle" | "rounded" }?
attlist.image &= [ a:defaultValue = "1" ] attribute colspan { text }?
+1
View File
@@ -609,6 +609,7 @@
<choice>
<value>icon</value>
<value>url</value>
<value>color</value>
</choice>
</attribute>
</optional>
+1 -1
View File
@@ -80,7 +80,7 @@ suffix = element suffix { attlist.affix, empty }
attlist.affix &= attribute string { text }?
attlist.affix &= ( attribute name { text } | attribute id { text } )
attlist.affix &= attribute icon { text }?
attlist.affix &= [a:defaultValue = "icon"] attribute icon_type { "icon" | "url" }?
attlist.affix &= [a:defaultValue = "icon"] attribute icon_type { "icon" | "url" | "color" }?
attlist.affix &= attribute url_size { text }?
attlist.affix &= [a:defaultValue = "square"] attribute border { "square" | "circle" | "rounded" }?
button = element button { attlist.button, empty }
+1
View File
@@ -379,6 +379,7 @@
<choice>
<value>icon</value>
<value>url</value>
<value>color</value>
</choice>
</attribute>
</optional>