draw: remove custom markup support

Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
Julien Danjou 2008-12-03 15:23:10 +01:00
parent a4d914f0e4
commit 26da80f90a
17 changed files with 192 additions and 496 deletions

View File

@ -64,7 +64,6 @@ set(AWE_SRCS
${SOURCE_DIR}/swindow.c ${SOURCE_DIR}/swindow.c
${SOURCE_DIR}/common/buffer.c ${SOURCE_DIR}/common/buffer.c
${SOURCE_DIR}/common/atoms.c ${SOURCE_DIR}/common/atoms.c
${SOURCE_DIR}/common/markup.c
${SOURCE_DIR}/common/socket.c ${SOURCE_DIR}/common/socket.c
${SOURCE_DIR}/common/util.c ${SOURCE_DIR}/common/util.c
${SOURCE_DIR}/common/version.c ${SOURCE_DIR}/common/version.c

1
README
View File

@ -14,7 +14,6 @@ In order to build awesome itself, you need header files and libs of:
- cairo built with xcb support - cairo built with xcb support
- pango and pangocairo - pango and pangocairo
- libev - libev
- glib
- Imlib2 - Imlib2
- dbus (optional, use -DWITH_DBUS=OFF with cmake to disable) - dbus (optional, use -DWITH_DBUS=OFF with cmake to disable)
- gperf - gperf

View File

@ -37,22 +37,13 @@ alpha channel to `aa' and will blend the green with the color under it.
TEXT FORMAT TEXT FORMAT
----------- -----------
You can use and mix Pango markup and awesome markup in text string. You can use Pango markup in text string.
This allows to format the text rendered in widgets. This allows to format the text rendered in widgets.
Pango markup documentation can be found in the Pango documentation at Pango markup documentation can be found in the Pango documentation at
http://library.gnome.org/devel/pango/stable/PangoMarkupFormat.html. http://library.gnome.org/devel/pango/stable/PangoMarkupFormat.html.
List of *awesome* markup elements and their attributes:
* bg
- image: path to a background image
- align: background image alignment
- resize: resize background image to text size
A Pango markup example: <span weight="bold" foreground="#336699">...</span>. A Pango markup example: <span weight="bold" foreground="#336699">...</span>.
An *awesome* markup example: <text align="right"/>.
SEE ALSO SEE ALSO
-------- --------
awesome(1) awesome-client(1) awesome(1) awesome-client(1)

View File

@ -104,7 +104,7 @@ mymainmenu = awful.menu.new({ items = { { "awesome", myawesomemenu, beautiful.aw
} }
}) })
mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon, mylauncher = awful.widget.launcher({ image = image(beautiful.awesome_icon),
menu = mymainmenu }) menu = mymainmenu })
-- Create a systray -- Create a systray

View File

@ -30,7 +30,6 @@
#include "systray.h" #include "systray.h"
#include "property.h" #include "property.h"
#include "wibox.h" #include "wibox.h"
#include "common/markup.h"
#include "common/atoms.h" #include "common/atoms.h"
extern awesome_t globalconf; extern awesome_t globalconf;

View File

@ -1,182 +0,0 @@
/*
* markup.c - markup functions
*
* Copyright © 2008 Julien Danjou <julien@danjou.info>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <glib.h>
#include "common/markup.h"
/** Callback to invoke when the opening tag of an element is seen.
* Here is just copies element elements we do not care about, and copies
* values we care.
* \param context the context of GMarkup
* \param element_name element name
* \param attribute_names array of attribute names
* \param attribute_values array of attribute values
* \param user_data pointer to user data, here a markup_parser_data_t pointer
* \param error a GError
*/
static void
markup_parse_start_element(GMarkupParseContext *context __attribute__ ((unused)),
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error __attribute__ ((unused)))
{
markup_parser_data_t *p = (markup_parser_data_t *) user_data;
int i;
for(i = 0; p->elements[i]; i++)
if(!a_strcmp(element_name, p->elements[i])) {
(*p->on_element)(p, element_name, attribute_names,
attribute_values);
return;
}
if(a_strcmp(element_name, "markup"))
{
buffer_addf(&p->text, "<%s", element_name);
for(i = 0; attribute_names[i]; i++)
{
buffer_addf(&p->text, " %s=\"%s\"", attribute_names[i],
attribute_values[i]);
}
buffer_addc(&p->text, '>');
}
}
/** Callback to invoke when the closing tag of an element is seen. Note that
* this is also called for empty tags like \<empty/\>.
* Here is just copies element elements we do not care about.
* \param context the context of GMarkup
* \param element_name element name
* \param user_data pointer to user data, here a markup_parser_data_t pointer
* \param error a GError
*/
static void
markup_parse_end_element(GMarkupParseContext *context __attribute__ ((unused)),
const gchar *element_name,
gpointer user_data,
GError **error __attribute__ ((unused)))
{
markup_parser_data_t *p = (markup_parser_data_t *) user_data;
int i;
for(i = 0; p->elements[i]; i++)
if(!a_strcmp(element_name, p->elements[i]))
return;
if(a_strcmp(element_name, "markup"))
buffer_addf(&p->text, "</%s>", element_name);
}
/** Callback to invoke when some text is seen (text is always inside an
* element). Note that the text of an element may be spread over multiple calls
* of this function. If the G_MARKUP_TREAT_CDATA_AS_TEXT flag is set, this
* function is also called for the content of CDATA marked sections.
* Here it recopies blindly the text in the text attribute of user_data.
* \param context the context of GMarkup
* \param text the text
* \param text_len the text length
* \param user_data pointer to user data, here a markup_parser_data_t pointer
* \param error a GError
*/
static void
markup_parse_text(GMarkupParseContext *context __attribute__ ((unused)),
const gchar *text,
gsize text_len,
gpointer user_data,
GError **error __attribute__ ((unused)))
{
markup_parser_data_t *p = (markup_parser_data_t *) user_data;
if (text_len) {
buffer_grow(&p->text, text_len);
buffer_add_xmlescaped(&p->text, text);
}
}
/** Create a markup_parser_data_t structure with elements list.
* \param a pointer to an allocated markup_parser_data_t which must be wiped
* with markup_parser_data_wipe()
*/
void markup_parser_data_init(markup_parser_data_t *p)
{
buffer_init(&p->text);
}
/** Wipe a markup_parser_data_t initialized with markup_parser_data_init.
* \param p markup_parser_data_t address
*/
void
markup_parser_data_wipe(markup_parser_data_t *p)
{
buffer_wipe(&p->text);
}
/** Parse markup defined in data on the string str.
* \param data A markup_parser_data_t allocated by markup_parser_data_new().
* \param str A string to parse markup from.
* \param slen String length.
* \return True if success, false otherwise.
*/
bool
markup_parse(markup_parser_data_t *data, const char *str, ssize_t slen)
{
static GMarkupParser const parser =
{
/* start_element */
markup_parse_start_element,
/* end_element */
markup_parse_end_element,
/* text */
markup_parse_text,
/* passthrough */
NULL,
/* error */
NULL
};
GMarkupParseContext *mkp_ctx;
GError *error = NULL;
if(slen <= 0)
return false;
mkp_ctx = g_markup_parse_context_new(&parser, 0, data, NULL);
if(!g_markup_parse_context_parse(mkp_ctx, "<markup>", -1, &error)
|| !g_markup_parse_context_parse(mkp_ctx, str, slen, &error)
|| !g_markup_parse_context_parse(mkp_ctx, "</markup>", -1, &error)
|| !g_markup_parse_context_end_parse(mkp_ctx, &error))
{
warn("unable to parse text \"%s\": %s", str, error ? error->message : "unknown error");
if(error)
g_error_free(error);
g_markup_parse_context_free(mkp_ctx);
return false;
}
g_markup_parse_context_free(mkp_ctx);
return true;
}
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -1,45 +0,0 @@
/*
* markup.h - markup header
*
* Copyright © 2008 Julien Danjou <julien@danjou.info>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef AWESOME_COMMON_MARKUP_H
#define AWESOME_COMMON_MARKUP_H
#include "common/buffer.h"
typedef struct markup_parser_data_t markup_parser_data_t;
typedef void (markup_on_elem_f)(markup_parser_data_t *, const char *,
const char **, const char **);
struct markup_parser_data_t
{
buffer_t text;
const char * const *elements;
markup_on_elem_f *on_element;
void *priv;
};
void markup_parser_data_init(markup_parser_data_t *);
void markup_parser_data_wipe(markup_parser_data_t *);
bool markup_parse(markup_parser_data_t *data, const char *, ssize_t);
#endif
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -1,8 +1,10 @@
1
align align
bar_data_add bar_data_add
bar_properties_set bar_properties_set
bg bg
bg_align
bg_image
bg_resize
border_color border_color
border_padding border_padding
border_width border_width
@ -57,7 +59,6 @@ Mod5
mouse_enter mouse_enter
mouse_leave mouse_leave
name name
on
ontop ontop
opacity opacity
orientation orientation
@ -84,7 +85,6 @@ ticks_gap
titlebar titlebar
top top
transient_for transient_for
true
type type
urgent urgent
valign valign
@ -96,4 +96,3 @@ word
word_char word_char
workarea workarea
wrap wrap
yes

View File

@ -37,8 +37,6 @@
#include <alloca.h> #include <alloca.h>
#endif #endif
#include "tokenize.h"
typedef enum typedef enum
{ {
East, East,
@ -313,29 +311,6 @@ a_strncat(char *dst, ssize_t n, const char *src, ssize_t l)
return dlen + a_strncpy(dst + dlen, n - dlen, src, l); return dlen + a_strncpy(dst + dlen, n - dlen, src, l);
} }
/** \brief convert a string to a boolean value.
*
* The a_strtobool() function converts a string \c s into a boolean.
* It recognizes the strings "true", "on", "yes" and "1".
*
* \param[in] s the string to convert
* \return true if the string is recognized as possibly true, false otherwise.
*/
static inline bool
a_strtobool(const char *s, ssize_t len)
{
switch(a_tokenize(s, len))
{
case A_TK_TRUE:
case A_TK_YES:
case A_TK_ON:
case A_TK_1:
return true;
default:
return false;
}
}
#define fatal(string, ...) _fatal(__LINE__, \ #define fatal(string, ...) _fatal(__LINE__, \
__FUNCTION__, \ __FUNCTION__, \
string, ## __VA_ARGS__) string, ## __VA_ARGS__)

View File

@ -27,6 +27,7 @@
#include "common/xutil.h" #include "common/xutil.h"
#include "common/atoms.h" #include "common/atoms.h"
#include "common/tokenize.h"
/** Get the string value of an atom. /** Get the string value of an atom.
* \param conn X connection. * \param conn X connection.

163
draw.c
View File

@ -32,7 +32,6 @@
#include "structs.h" #include "structs.h"
#include "common/tokenize.h" #include "common/tokenize.h"
#include "common/markup.h"
extern awesome_t globalconf; extern awesome_t globalconf;
@ -159,81 +158,28 @@ draw_font_delete(font_t **font)
} }
} }
static void /** Initialize a draw_text_context_t with text data.
draw_markup_on_element(markup_parser_data_t *p, const char *elem, * \param data The draw text context to init.
const char **names, const char **values) * \param str The text string to render.
* \param slen The text string length.
* \return True if everything is ok, false otherwise.
*/
bool
draw_text_context_init(draw_text_context_t *data, const char *str, ssize_t slen)
{ {
draw_parser_data_t *data = p->priv;
/* hack: markup.c validates tags so we can avoid strcmps here */
switch (*elem) {
case 'b':
if(elem[2] == '_') /* bg_margin */
for(; *names; names++, values++)
switch(a_tokenize(*names, -1))
{
case A_TK_LEFT:
data->bg_margin.left = atoi(*values);
break;
case A_TK_TOP:
data->bg_margin.top = atoi(*values);
default:
break;
}
else /* bg */
for(; *names; names++, values++)
switch(a_tokenize(*names, -1))
{
case A_TK_IMAGE:
if(data->bg_image)
image_delete(&data->bg_image);
data->bg_image = image_new_from_file(*values);
break;
case A_TK_ALIGN:
data->bg_align = draw_align_fromstr(*values, -1);
break;
case A_TK_RESIZE:
data->bg_resize = a_strtobool(*values, -1);
default:
break;
}
break;
}
}
static bool
draw_text_markup_expand(draw_parser_data_t *data,
const char *str, ssize_t slen)
{
static char const * const elements[] = { "bg", "bg_margin", NULL };
markup_parser_data_t p =
{
.elements = elements,
.priv = data,
.on_element = &draw_markup_on_element,
};
GError *error = NULL; GError *error = NULL;
bool ret = false;
markup_parser_data_init(&p); if(!pango_parse_markup(str, slen, 0, &data->attr_list, &data->text, NULL, &error))
if(!markup_parse(&p, str, slen))
goto bailout;
if(!pango_parse_markup(p.text.s, p.text.len, 0, &data->attr_list, &data->text, NULL, &error))
{ {
warn("cannot parse pango markup: %s", error ? error->message : "unknown error"); warn("cannot parse pango markup: %s", error ? error->message : "unknown error");
if(error) if(error)
g_error_free(error); g_error_free(error);
goto bailout; return false;
} }
data->len = a_strlen(data->text); data->len = a_strlen(data->text);
ret = true;
bailout: return true;
markup_parser_data_wipe(&p);
return ret;
} }
/** Initialize a new draw context. /** Initialize a new draw context.
@ -273,57 +219,17 @@ draw_context_init(draw_context_t *d, int phys_screen,
* \param align Text alignment. * \param align Text alignment.
* \param margin Margin to respect when drawing text. * \param margin Margin to respect when drawing text.
* \param area Area to draw to. * \param area Area to draw to.
* \param text Text to draw. * \param data Draw text context data.
* \param len Text to draw length.
* \param data Optional parser data.
* \param ext Text extents. * \param ext Text extents.
*/ */
void void
draw_text(draw_context_t *ctx, font_t *font, PangoEllipsizeMode ellip, PangoWrapMode wrap, draw_text(draw_context_t *ctx, draw_text_context_t *data, font_t *font,
alignment_t align, padding_t *margin, area_t area, const char *text, ssize_t len, PangoEllipsizeMode ellip, PangoWrapMode wrap,
draw_parser_data_t *pdata, area_t *ext) alignment_t align, padding_t *margin, area_t area, area_t *ext)
{ {
int x, y; int x, y;
draw_parser_data_t parser_data;
if(!pdata) pango_layout_set_text(ctx->layout, data->text, data->len);
{
draw_parser_data_init(&parser_data);
if(draw_text_markup_expand(&parser_data, text, len))
{
text = parser_data.text;
len = parser_data.len;
}
pdata = &parser_data;
}
else
{
text = pdata->text;
len = pdata->len;
}
if(pdata->bg_image)
{
x = area.x;
y = area.y;
switch(pdata->bg_align)
{
case AlignCenter:
x += (area.width - pdata->bg_image->width) / 2;
break;
case AlignRight:
x += area.width- pdata->bg_image->width;
break;
default:
break;
}
draw_image(ctx,
x + pdata->bg_margin.left,
y + pdata->bg_margin.top,
pdata->bg_resize ? area.height : 0, pdata->bg_image);
}
pango_layout_set_text(ctx->layout, pdata->text, pdata->len);
pango_layout_set_width(ctx->layout, pango_layout_set_width(ctx->layout,
pango_units_from_double(area.width pango_units_from_double(area.width
- (margin->left - (margin->left
@ -332,7 +238,7 @@ draw_text(draw_context_t *ctx, font_t *font, PangoEllipsizeMode ellip, PangoWrap
- (margin->top + margin->bottom)); - (margin->top + margin->bottom));
pango_layout_set_ellipsize(ctx->layout, ellip); pango_layout_set_ellipsize(ctx->layout, ellip);
pango_layout_set_wrap(ctx->layout, wrap); pango_layout_set_wrap(ctx->layout, wrap);
pango_layout_set_attributes(ctx->layout, pdata->attr_list); pango_layout_set_attributes(ctx->layout, data->attr_list);
pango_layout_set_font_description(ctx->layout, font->desc); pango_layout_set_font_description(ctx->layout, font->desc);
x = area.x + margin->left; x = area.x + margin->left;
@ -364,9 +270,6 @@ draw_text(draw_context_t *ctx, font_t *font, PangoEllipsizeMode ellip, PangoWrap
ctx->fg.alpha / 65535.0); ctx->fg.alpha / 65535.0);
pango_cairo_update_layout(ctx->cr, ctx->layout); pango_cairo_update_layout(ctx->cr, ctx->layout);
pango_cairo_show_layout(ctx->cr, ctx->layout); pango_cairo_show_layout(ctx->cr, ctx->layout);
if (pdata == &parser_data)
draw_parser_data_wipe(&parser_data);
} }
/** Setup color-source for cairo (gradient or mono). /** Setup color-source for cairo (gradient or mono).
@ -657,14 +560,13 @@ draw_graph_line(draw_context_t *ctx, area_t rect, int *to, int cur_index,
* \param y Y coordinate. * \param y Y coordinate.
* \param w Width. * \param w Width.
* \param h Height. * \param h Height.
* \param wanted_h Wanted height: if > 0, image will be resized. * \param ratio The ratio to apply to the image.
* \param data The image pixels array. * \param data The image pixels array.
*/ */
static void static void
draw_image_from_argb_data(draw_context_t *ctx, int x, int y, int w, int h, draw_image_from_argb_data(draw_context_t *ctx, int x, int y, int w, int h,
int wanted_h, unsigned char *data) double ratio, unsigned char *data)
{ {
double ratio;
cairo_t *cr; cairo_t *cr;
cairo_surface_t *source; cairo_surface_t *source;
@ -675,14 +577,8 @@ draw_image_from_argb_data(draw_context_t *ctx, int x, int y, int w, int h,
cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w)); cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w));
#endif #endif
cr = cairo_create(ctx->surface); cr = cairo_create(ctx->surface);
if(wanted_h > 0 && h > 0)
{
ratio = (double) wanted_h / (double) h;
cairo_scale(cr, ratio, ratio); cairo_scale(cr, ratio, ratio);
cairo_set_source_surface(cr, source, x / ratio, y / ratio); cairo_set_source_surface(cr, source, x / ratio, y / ratio);
}
else
cairo_set_source_surface(cr, source, x, y);
cairo_paint(cr); cairo_paint(cr);
@ -694,13 +590,13 @@ draw_image_from_argb_data(draw_context_t *ctx, int x, int y, int w, int h,
* \param ctx Draw context to draw to. * \param ctx Draw context to draw to.
* \param x X coordinate. * \param x X coordinate.
* \param y Y coordinate. * \param y Y coordinate.
* \param wanted_h Wanted height: if > 0, image will be resized. * \param ratio The ratio to apply to the image.
* \param image The image to draw. * \param image The image to draw.
*/ */
void void
draw_image(draw_context_t *ctx, int x, int y, int wanted_h, image_t *image) draw_image(draw_context_t *ctx, int x, int y, double ratio, image_t *image)
{ {
draw_image_from_argb_data(ctx, x, y, image->width, image->height, wanted_h, image->data); draw_image_from_argb_data(ctx, x, y, image->width, image->height, ratio, image->data);
} }
/** Rotate a pixmap. /** Rotate a pixmap.
@ -743,14 +639,12 @@ draw_rotate(draw_context_t *ctx,
} }
/** Return the width and height of a text in pixel. /** Return the width and height of a text in pixel.
* \param data The draw context text data.
* \param font Font to use. * \param font Font to use.
* \param text The text.
* \param len The text length.
* \param pdata The parser data to fill.
* \return Text height and width. * \return Text height and width.
*/ */
area_t area_t
draw_text_extents(font_t *font, const char *text, ssize_t len, draw_parser_data_t *parser_data) draw_text_extents(draw_text_context_t *data, font_t *font)
{ {
cairo_surface_t *surface; cairo_surface_t *surface;
cairo_t *cr; cairo_t *cr;
@ -759,10 +653,7 @@ draw_text_extents(font_t *font, const char *text, ssize_t len, draw_parser_data_
xcb_screen_t *s = xutil_screen_get(globalconf.connection, globalconf.default_screen); xcb_screen_t *s = xutil_screen_get(globalconf.connection, globalconf.default_screen);
area_t geom = { 0, 0, 0, 0 }; area_t geom = { 0, 0, 0, 0 };
if(!len) if(data->len <= 0)
return geom;
if(!draw_text_markup_expand(parser_data, text, len))
return geom; return geom;
surface = cairo_xcb_surface_create(globalconf.connection, surface = cairo_xcb_surface_create(globalconf.connection,
@ -773,8 +664,8 @@ draw_text_extents(font_t *font, const char *text, ssize_t len, draw_parser_data_
cr = cairo_create(surface); cr = cairo_create(surface);
layout = pango_cairo_create_layout(cr); layout = pango_cairo_create_layout(cr);
pango_layout_set_text(layout, parser_data->text, parser_data->len); pango_layout_set_text(layout, data->text, data->len);
pango_layout_set_attributes(layout, parser_data->attr_list); pango_layout_set_attributes(layout, data->attr_list);
pango_layout_set_font_description(layout, font->desc); pango_layout_set_font_description(layout, font->desc);
pango_layout_get_pixel_extents(layout, NULL, &ext); pango_layout_get_pixel_extents(layout, NULL, &ext);
g_object_unref(layout); g_object_unref(layout);

26
draw.h
View File

@ -169,16 +169,10 @@ typedef struct
PangoAttrList *attr_list; PangoAttrList *attr_list;
char *text; char *text;
ssize_t len; ssize_t len;
struct } draw_text_context_t;
{
int top, left;
} bg_margin;
image_t *bg_image;
alignment_t bg_align;
bool bg_resize;
} draw_parser_data_t;
void draw_text(draw_context_t *, font_t *, PangoEllipsizeMode, PangoWrapMode, alignment_t, padding_t *, area_t, const char *, ssize_t len, draw_parser_data_t *, area_t *); bool draw_text_context_init(draw_text_context_t *, const char *, ssize_t);
void draw_text(draw_context_t *, draw_text_context_t *, font_t *, PangoEllipsizeMode, PangoWrapMode, alignment_t, padding_t *, area_t, area_t *);
void draw_rectangle(draw_context_t *, area_t, float, bool, const xcolor_t *); void draw_rectangle(draw_context_t *, area_t, float, bool, const xcolor_t *);
void draw_rectangle_gradient(draw_context_t *, area_t, float, bool, vector_t, void draw_rectangle_gradient(draw_context_t *, area_t, float, bool, vector_t,
const xcolor_t *, const xcolor_t *, const xcolor_t *); const xcolor_t *, const xcolor_t *, const xcolor_t *);
@ -188,9 +182,9 @@ void draw_graph(draw_context_t *, area_t, int *, int *, int, position_t, vector_
const xcolor_t *, const xcolor_t *, const xcolor_t *); const xcolor_t *, const xcolor_t *, const xcolor_t *);
void draw_graph_line(draw_context_t *, area_t, int *, int, position_t, vector_t, void draw_graph_line(draw_context_t *, area_t, int *, int, position_t, vector_t,
const xcolor_t *, const xcolor_t *, const xcolor_t *); const xcolor_t *, const xcolor_t *, const xcolor_t *);
void draw_image(draw_context_t *, int, int, int, image_t *); void draw_image(draw_context_t *, int, int, double, image_t *);
void draw_rotate(draw_context_t *, xcb_drawable_t, xcb_drawable_t, int, int, int, int, double, int, int); void draw_rotate(draw_context_t *, xcb_drawable_t, xcb_drawable_t, int, int, int, int, double, int, int);
area_t draw_text_extents(font_t *, const char *, ssize_t, draw_parser_data_t *); area_t draw_text_extents(draw_text_context_t *, font_t *);
alignment_t draw_align_fromstr(const char *, ssize_t); alignment_t draw_align_fromstr(const char *, ssize_t);
const char *draw_align_tostr(alignment_t); const char *draw_align_tostr(alignment_t);
@ -212,19 +206,13 @@ xcolor_init_request_t xcolor_init_unchecked(xcolor_t *, const char *, ssize_t);
bool xcolor_init_reply(xcolor_init_request_t); bool xcolor_init_reply(xcolor_init_request_t);
static inline void static inline void
draw_parser_data_init(draw_parser_data_t *pdata) draw_text_context_wipe(draw_text_context_t *pdata)
{
p_clear(pdata, 1);
}
static inline void
draw_parser_data_wipe(draw_parser_data_t *pdata)
{ {
if(pdata) if(pdata)
{ {
if(pdata->attr_list)
pango_attr_list_unref(pdata->attr_list); pango_attr_list_unref(pdata->attr_list);
p_delete(&pdata->text); p_delete(&pdata->text);
image_unref(&pdata->bg_image);
} }
} }

View File

@ -6,6 +6,7 @@
-- Grab environment we need -- Grab environment we need
local math = math local math = math
local image = image
local pairs = pairs local pairs = pairs
local otable = otable local otable = otable
local capi = local capi =
@ -72,13 +73,13 @@ function add(c, args)
local closef local closef
if theme.titlebar_close_button_focus then if theme.titlebar_close_button_focus then
closef = widget.button({ align = "right", closef = widget.button({ align = "right",
image = theme.titlebar_close_button_focus }) image = image(theme.titlebar_close_button_focus) })
end end
local close local close
if theme.titlebar_close_button_normal then if theme.titlebar_close_button_normal then
close = widget.button({ align = "right", close = widget.button({ align = "right",
image = theme.titlebar_close_button_normal }) image = image(theme.titlebar_close_button_normal) })
end end
if close or closef then if close or closef then

View File

@ -15,6 +15,7 @@ local capi =
client = client, client = client,
button = button, button = button,
widget = widget, widget = widget,
image = image,
mouse = mouse mouse = mouse
} }
local util = require("awful.util") local util = require("awful.util")
@ -51,7 +52,10 @@ local function taglist_update (screen, w, label, buttons, data)
end end
-- Update widgets text -- Update widgets text
for k, tag in ipairs(tags) do for k, tag in ipairs(tags) do
w[k].text, w[k].bg = label(tag) local text, bg, bg_image, bg_resize = label(tag)
w[k].text = text
if text then
w[k].bg, w[k].bg_image, w[k].bg_resize = bg, bg_image, bg_resize
if buttons then if buttons then
if not data[tag] then if not data[tag] then
-- Replace press function by a new one calling with tags as -- Replace press function by a new one calling with tags as
@ -67,6 +71,7 @@ local function taglist_update (screen, w, label, buttons, data)
w[k]:buttons(data[tag]) w[k]:buttons(data[tag])
end end
end end
end
end end
--- Create a new taglist widget. --- Create a new taglist widget.
@ -112,7 +117,8 @@ end
-- squares_sel Optional: a user provided image for selected squares. -- squares_sel Optional: a user provided image for selected squares.
-- squares_unsel Optional: a user provided image for unselected squares. -- squares_unsel Optional: a user provided image for unselected squares.
-- squares_resize Optional: true or false to resize squares. -- squares_resize Optional: true or false to resize squares.
-- @return A string to print and a background color. -- @return A string to print, a background color, a background image and a
-- background resize value.
function taglist.label.all(t, args) function taglist.label.all(t, args)
if not args then args = {} end if not args then args = {} end
local theme = beautiful.get() local theme = beautiful.get()
@ -124,23 +130,26 @@ function taglist.label.all(t, args)
local taglist_squares_unsel = args.squares_unsel or theme.taglist_squares_unsel local taglist_squares_unsel = args.squares_unsel or theme.taglist_squares_unsel
local taglist_squares_resize = theme.taglist_squares_resize or args.squares_resize or "true" local taglist_squares_resize = theme.taglist_squares_resize or args.squares_resize or "true"
local font = args.font or theme.taglist_font or theme.font or "" local font = args.font or theme.taglist_font or theme.font or ""
local text = "<span font_desc='"..font.."'>" local text = "<span font_desc='"..font.."'> "
local background = ""
local sel = capi.client.focus local sel = capi.client.focus
local bg_color = nil local bg_color = nil
local fg_color = nil local fg_color = nil
local bg_image
local bg_resize = false
if t.selected then if t.selected then
bg_color = bg_focus bg_color = bg_focus
fg_color = fg_focus fg_color = fg_focus
end end
if sel and sel:tags()[t] then if sel and sel:tags()[t] then
if taglist_squares_sel then if taglist_squares_sel then
background = "resize=\"" .. taglist_squares_resize .. "\" image=\"" .. taglist_squares_sel .. "\"" bg_image = capi.image(taglist_squares_sel)
bg_resize = taglist_squares_resize == "true"
end end
else else
local cls = t:clients() local cls = t:clients()
if #cls > 0 and taglist_squares_unsel then if #cls > 0 and taglist_squares_unsel then
background = "resize=\"" .. taglist_squares_resize .. "\" image=\"" .. taglist_squares_unsel .. "\"" bg_image = capi.image(taglist_squares_unsel)
bg_resize = taglist_squares_resize == "true"
end end
for k, c in pairs(cls) do for k, c in pairs(cls) do
if c.urgent then if c.urgent then
@ -151,12 +160,12 @@ function taglist.label.all(t, args)
end end
end end
if fg_color then if fg_color then
text = text .. "<bg "..background.." /> <span color='"..util.color_strip_alpha(fg_color).."'>"..util.escape(t.name).."</span> " text = text .. "<span color='"..util.color_strip_alpha(fg_color).."'>"..util.escape(t.name).."</span>"
else else
text = text .. " <bg "..background.." />"..util.escape(t.name).." " text = text .. util.escape(t.name)
end end
text = text .. "</span>" text = text .. " </span>"
return text, bg_color return text, bg_color, bg_image, bg_resize
end end
--- Return labels for a taglist widget with all *non empty* tags from screen. --- Return labels for a taglist widget with all *non empty* tags from screen.
@ -168,7 +177,8 @@ end
-- fg_focus The foreground color for selected tag. -- fg_focus The foreground color for selected tag.
-- bg_urgent The background color for urgent tags. -- bg_urgent The background color for urgent tags.
-- fg_urgent The foreground color for urgent tags. -- fg_urgent The foreground color for urgent tags.
-- @return A string to print. -- @return A string to print, a background color, a background image and a
-- background resize value.
function taglist.label.noempty(t, args) function taglist.label.noempty(t, args)
if #t:clients() > 0 or t.selected then if #t:clients() > 0 or t.selected then
return taglist.label.all(t, args) return taglist.label.all(t, args)
@ -195,6 +205,8 @@ local function tasklist_update(w, buttons, label, data)
w[i] = capi.widget({ type = "imagebox", align = "flex" }) w[i] = capi.widget({ type = "imagebox", align = "flex" })
w[i + 1] = capi.widget({ type = "textbox", align = "flex" }) w[i + 1] = capi.widget({ type = "textbox", align = "flex" })
w[i + 1]:margin({ left = 2, right = 2 }) w[i + 1]:margin({ left = 2, right = 2 })
w[i + 1].bg_resize = true
w[i + 1].bg_align = "right"
end end
-- Remove widgets -- Remove widgets
elseif len > #clients then elseif len > #clients then
@ -220,7 +232,7 @@ local function tasklist_update(w, buttons, label, data)
w[k]:buttons(data[c]) w[k]:buttons(data[c])
w[k + 1]:buttons(data[c]) w[k + 1]:buttons(data[c])
end end
w[k + 1].text, w[k + 1].bg = label(clients[(k + 1) / 2]) w[k + 1].text, w[k + 1].bg, w[k + 1].bg_image= label(clients[(k + 1) / 2])
w[k].bg = w[k + 1].bg w[k].bg = w[k + 1].bg
if w[k + 1].text then if w[k + 1].text then
-- Set icon -- Set icon
@ -275,13 +287,13 @@ local function widget_tasklist_label_common(c, args)
local fg_urgent = args.fg_urgent or theme.tasklist_fg_urgent or theme.fg_urgent local fg_urgent = args.fg_urgent or theme.tasklist_fg_urgent or theme.fg_urgent
local bg_urgent = args.bg_urgent or theme.tasklist_bg_urgent or theme.bg_urgent local bg_urgent = args.bg_urgent or theme.tasklist_bg_urgent or theme.bg_urgent
local floating_icon = args.floating_icon or theme.tasklist_floating_icon local floating_icon = args.floating_icon or theme.tasklist_floating_icon
local floating_icon_align = args.floating_icon_align or theme.tasklist_floating_icon_align or "right"
local font = args.font or theme.tasklist_font or theme.font or "" local font = args.font or theme.tasklist_font or theme.font or ""
local bg = nil local bg = nil
local text = "<span font_desc='"..font.."'>" local text = "<span font_desc='"..font.."'>"
local name local name
local status_image
if client.floating.get(c) and floating_icon then if client.floating.get(c) and floating_icon then
text = text.."<bg image=\"" .. floating_icon .. "\" align=\"" .. floating_icon_align .. "\"/>" status_image = capi.image(floating_icon)
end end
if c.minimized then if c.minimized then
name = util.escape(c.icon_name) or "" name = util.escape(c.icon_name) or ""
@ -302,7 +314,7 @@ local function widget_tasklist_label_common(c, args)
text = text .. name text = text .. name
end end
text = text .. "</span>" text = text .. "</span>"
return text, bg return text, bg, status_image
end end
--- Return labels for a tasklist widget with clients from all tags and screen. --- Return labels for a tasklist widget with clients from all tags and screen.
@ -316,7 +328,7 @@ end
-- fg_focus The foreground color for focused client. -- fg_focus The foreground color for focused client.
-- bg_urgent The background color for urgent clients. -- bg_urgent The background color for urgent clients.
-- fg_urgent The foreground color for urgent clients. -- fg_urgent The foreground color for urgent clients.
-- @return A string to print and a background color. -- @return A string to print, a background color and a status image.
function tasklist.label.allscreen(c, screen, args) function tasklist.label.allscreen(c, screen, args)
return widget_tasklist_label_common(c, args) return widget_tasklist_label_common(c, args)
end end
@ -332,7 +344,7 @@ end
-- fg_focus The foreground color for focused client. -- fg_focus The foreground color for focused client.
-- bg_urgent The background color for urgent clients. -- bg_urgent The background color for urgent clients.
-- fg_urgent The foreground color for urgent clients. -- fg_urgent The foreground color for urgent clients.
-- @return A string to print and a background color. -- @return A string to print, a background color and a status image.
function tasklist.label.alltags(c, screen, args) function tasklist.label.alltags(c, screen, args)
-- Only print client on the same screen as this widget -- Only print client on the same screen as this widget
if c.screen ~= screen then return end if c.screen ~= screen then return end
@ -350,7 +362,7 @@ end
-- fg_focus The foreground color for focused client. -- fg_focus The foreground color for focused client.
-- bg_urgent The background color for urgent clients. -- bg_urgent The background color for urgent clients.
-- fg_urgent The foreground color for urgent clients. -- fg_urgent The foreground color for urgent clients.
-- @return A string to print and a background color. -- @return A string to print, a background color and a status image.
function tasklist.label.currenttags(c, screen, args) function tasklist.label.currenttags(c, screen, args)
-- Only print client on the same screen as this widget -- Only print client on the same screen as this widget
if c.screen ~= screen then return end if c.screen ~= screen then return end
@ -367,14 +379,14 @@ end
-- @return A textbox widget configured as a button. -- @return A textbox widget configured as a button.
function button(args) function button(args)
if not args or not args.image then return end if not args or not args.image then return end
args.type = "textbox" local img_release = args.image
local img_press = img_release:crop(-2, -2, img_release.width, img_release.height)
args.type = "imagebox"
local w = capi.widget(args) local w = capi.widget(args)
local img_release = "<bg image=\"" .. args.image .. "\" resize=\"true\"/>" w.image = img_release
local img_press = "<bg_margin top=\"2\" left=\"2\"/><bg image=\"" .. args.image .. "\" resize=\"true\"/>" w:buttons({ capi.button({}, 1, function () w.image = img_press end, function () w.image = img_release end) })
w.text = img_release function w.mouse_leave(s) w.image = img_release end
w:buttons({ capi.button({}, 1, function () w.text = img_press end, function () w.text = img_release end) }) function w.mouse_enter(s) if capi.mouse.coords().buttons[1] then w.image = img_press end end
function w.mouse_leave(s) w.text = img_release end
function w.mouse_enter(s) if capi.mouse.coords().buttons[1] then w.text = img_press end end
return w return w
end end

View File

@ -32,6 +32,7 @@
#include "common/xutil.h" #include "common/xutil.h"
#include "common/xembed.h" #include "common/xembed.h"
#include "common/refcount.h" #include "common/refcount.h"
#include "common/tokenize.h"
/** Windows type */ /** Windows type */
typedef enum typedef enum

View File

@ -44,13 +44,15 @@ imagebox_geometry(widget_t *widget, int screen, int height, int width)
{ {
if(d->resize) if(d->resize)
{ {
geometry.width = ((double) height / (double) d->image->height) * d->image->width; double ratio = (double) height / d->image->height;
geometry.height = height; geometry.width = ratio * d->image->width;
if(geometry.width > width) if(geometry.width > width)
{ {
geometry.width = 0; geometry.width = 0;
geometry.height = 0; geometry.height = 0;
} }
else
geometry.height = height;
} }
else if(d->image->width <= width) else if(d->image->width <= width)
{ {
@ -91,6 +93,7 @@ imagebox_draw(widget_t *widget, draw_context_t *ctx, area_t geometry,
draw_rectangle(ctx, geometry, 1.0, true, &d->bg); draw_rectangle(ctx, geometry, 1.0, true, &d->bg);
int y = geometry.y; int y = geometry.y;
double ratio = d->resize ? (double) geometry.height / d->image->height : 1;
switch(d->valign) switch(d->valign)
{ {
case AlignBottom: case AlignBottom:
@ -103,7 +106,7 @@ imagebox_draw(widget_t *widget, draw_context_t *ctx, area_t geometry,
break; break;
} }
draw_image(ctx, geometry.x, y, d->resize ? ctx->height : 0, d->image); draw_image(ctx, geometry.x, y, ratio, d->image);
} }
} }

View File

@ -27,18 +27,13 @@ extern awesome_t globalconf;
/** The textbox private data structure */ /** The textbox private data structure */
typedef struct typedef struct
{ {
/** Textbox text */ draw_text_context_t data;
char *text;
/** Textbox text length */
ssize_t len;
/** Textbox width */ /** Textbox width */
int width; int width;
/** Extents */ /** Extents */
area_t extents; area_t extents;
PangoEllipsizeMode ellip; PangoEllipsizeMode ellip;
PangoWrapMode wrap; PangoWrapMode wrap;
/** Draw parser data */
draw_parser_data_t pdata;
/** Border */ /** Border */
struct struct
{ {
@ -51,6 +46,12 @@ typedef struct
padding_t margin; padding_t margin;
/** Background color */ /** Background color */
xcolor_t bg; xcolor_t bg;
/** Background image */
image_t *bg_image;
/** Background resize to wibox height. */
bool bg_resize;
/** Background alignment */
alignment_t bg_align;
} textbox_data_t; } textbox_data_t;
static area_t static area_t
@ -65,14 +66,13 @@ textbox_geometry(widget_t *widget, int screen, int height, int width)
geometry.width = d->width; geometry.width = d->width;
else if(widget->align == AlignFlex) else if(widget->align == AlignFlex)
geometry.width = width; geometry.width = width;
else else if(d->bg_image)
{ {
geometry.width = MIN(d->extents.width, width); double ratio = d->bg_resize ? (double) geometry.height / d->bg_image->height : 1;
geometry.width = MIN(width, MAX(d->extents.width, MAX(d->width, d->bg_image->width * ratio)));
if(d->pdata.bg_image)
geometry.width = MAX(geometry.width,
d->pdata.bg_resize ? ((double) d->pdata.bg_image->width / (double) d->pdata.bg_image->height) * geometry.height : d->pdata.bg_image->width);
} }
else
geometry.width = MIN(d->extents.width, width);
return geometry; return geometry;
} }
@ -95,8 +95,30 @@ textbox_draw(widget_t *widget, draw_context_t *ctx, area_t geometry,
if(d->border.width > 0) if(d->border.width > 0)
draw_rectangle(ctx, geometry, d->border.width, false, &d->border.color); draw_rectangle(ctx, geometry, d->border.width, false, &d->border.color);
draw_text(ctx, globalconf.font, d->ellip, d->wrap, d->align, &d->margin, if(d->bg_image)
geometry, d->text, d->len, &d->pdata, &d->extents); {
double ratio = d->bg_resize ? (double) geometry.height / d->bg_image->height : 1;
/* check there is enough space to draw the image */
if(ratio * d->bg_image->width <= geometry.width)
{
int x = geometry.x;
int y = geometry.y;
switch(d->bg_align)
{
case AlignCenter:
x += (geometry.width - d->bg_image->width * ratio) / 2;
break;
case AlignRight:
x += geometry.width - d->bg_image->width * ratio;
break;
default:
break;
}
draw_image(ctx, x, y, ratio, d->bg_image);
}
}
draw_text(ctx, &d->data, globalconf.font, d->ellip, d->wrap, d->align, &d->margin, geometry, &d->extents);
} }
/** Delete a textbox widget. /** Delete a textbox widget.
@ -106,8 +128,7 @@ static void
textbox_destructor(widget_t *w) textbox_destructor(widget_t *w)
{ {
textbox_data_t *d = w->data; textbox_data_t *d = w->data;
draw_parser_data_wipe(&d->pdata); draw_text_context_wipe(&d->data);
p_delete(&d->text);
p_delete(&d); p_delete(&d);
} }
@ -140,6 +161,9 @@ luaA_textbox_margin(lua_State *L)
* \lfield align Text alignment, left, center or right. * \lfield align Text alignment, left, center or right.
* \lfield margin Method to pass text margin: a table with top, left, right and bottom keys. * \lfield margin Method to pass text margin: a table with top, left, right and bottom keys.
* \lfield bg Background color. * \lfield bg Background color.
* \lfield bg_image Background image.
* \lfield bg_align Background image alignment.
* \lfield bg_resize Background resize.
*/ */
static int static int
luaA_textbox_index(lua_State *L, awesome_token_t token) luaA_textbox_index(lua_State *L, awesome_token_t token)
@ -149,6 +173,17 @@ luaA_textbox_index(lua_State *L, awesome_token_t token)
switch(token) switch(token)
{ {
case A_TK_BG_RESIZE:
lua_pushboolean(L, d->bg_resize);
return 1;
case A_TK_BG_ALIGN:
lua_pushstring(L, draw_align_tostr(d->bg_align));
return 1;
case A_TK_BG_IMAGE:
if(d->bg_image)
return luaA_image_userdata_new(L, d->bg_image);
else
return 0;
case A_TK_BG: case A_TK_BG:
return luaA_pushcolor(L, &d->bg); return luaA_pushcolor(L, &d->bg);
case A_TK_MARGIN: case A_TK_MARGIN:
@ -164,8 +199,12 @@ luaA_textbox_index(lua_State *L, awesome_token_t token)
luaA_pushcolor(L, &d->border.color); luaA_pushcolor(L, &d->border.color);
return 1; return 1;
case A_TK_TEXT: case A_TK_TEXT:
lua_pushstring(L, d->text); if(d->data.len > 0)
{
lua_pushlstring(L, d->data.text, d->data.len);
return 1; return 1;
}
return 0;
case A_TK_WIDTH: case A_TK_WIDTH:
lua_pushnumber(L, d->width); lua_pushnumber(L, d->width);
return 1; return 1;
@ -214,9 +253,29 @@ luaA_textbox_newindex(lua_State *L, awesome_token_t token)
widget_t **widget = luaA_checkudata(L, 1, "widget"); widget_t **widget = luaA_checkudata(L, 1, "widget");
const char *buf = NULL; const char *buf = NULL;
textbox_data_t *d = (*widget)->data; textbox_data_t *d = (*widget)->data;
image_t **image = NULL;
switch(token) switch(token)
{ {
case A_TK_BG_ALIGN:
buf = luaL_checklstring(L, 3, &len);
d->bg_align = draw_align_fromstr(buf, len);
break;
case A_TK_BG_RESIZE:
d->bg_resize = luaA_checkboolean(L, 3);
break;
case A_TK_BG_IMAGE:
if(lua_isnil(L, 3)
|| (image = luaA_checkudata(L, 3, "image")))
{
/* unref image */
image_unref(&d->bg_image);
if(image)
d->bg_image = image_ref(image);
else
d->bg_image = NULL;
}
break;
case A_TK_BG: case A_TK_BG:
if(lua_isnil(L, 3)) if(lua_isnil(L, 3))
p_clear(&d->bg, 1); p_clear(&d->bg, 1);
@ -239,21 +298,26 @@ luaA_textbox_newindex(lua_State *L, awesome_token_t token)
|| (buf = luaL_checklstring(L, 3, &len))) || (buf = luaL_checklstring(L, 3, &len)))
{ {
/* delete */ /* delete */
draw_parser_data_wipe(&d->pdata); draw_text_context_wipe(&d->data);
/* reinit since we are giving it as arg to draw_text unconditionally */ p_clear(&d->data, 1);
draw_parser_data_init(&d->pdata);
p_delete(&d->text);
if(buf) if(buf)
{ {
a_iso2utf8(buf, len, &d->text, &d->len); char *text;
d->extents = draw_text_extents(globalconf.font, d->text, d->len, &d->pdata); ssize_t tlen;
/* if text has been converted to UTF-8 */
if(draw_iso2utf8(buf, len, &text, &tlen))
{
draw_text_context_init(&d->data, text, tlen);
p_delete(&text);
} }
else else
{ draw_text_context_init(&d->data, buf, len);
d->len = 0;
p_clear(&d->extents, 1); d->extents = draw_text_extents(&d->data, globalconf.font);
} }
else
p_clear(&d->extents, 1);
} }
break; break;
case A_TK_WIDTH: case A_TK_WIDTH: