mirror of
https://github.com/davatorium/rofi.git
synced 2024-11-18 13:54:36 -05:00
Use cairo to do drawing.
This commit is contained in:
parent
29e81fdb07
commit
8f1f03e6d2
9 changed files with 398 additions and 424 deletions
|
@ -9,19 +9,19 @@ bin_PROGRAMS=rofi
|
|||
dist_bin_SCRIPTS=script/rofi-sensible-terminal
|
||||
|
||||
LIBS=\
|
||||
@xft_LIBS@\
|
||||
@x11_LIBS@\
|
||||
@xinerama_LIBS@\
|
||||
@libsn_LIBS@\
|
||||
@pango_LIBS@
|
||||
@pango_LIBS@\
|
||||
@cairo_LIBS@
|
||||
|
||||
AM_CFLAGS=\
|
||||
@EXTRA_CFLAGS@\
|
||||
@xft_CFLAGS@\
|
||||
@x11_CFLAGS@\
|
||||
@xinerama_CFLAGS@\
|
||||
@pango_CFLAGS@\
|
||||
@libsn_CFLAGS@\
|
||||
@cairo_CFLAGS@\
|
||||
-DMANPAGE_PATH="\"$(mandir)/\""\
|
||||
-I$(top_srcdir)/include/\
|
||||
-I$(top_srcdir)/config/\
|
||||
|
|
|
@ -48,10 +48,10 @@ AS_IF([test "x$enable_i3support" != xno && test "x$enable_windowmode" != "xno"],
|
|||
##
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
PKG_CHECK_MODULES([xft], [xft])
|
||||
PKG_CHECK_MODULES([x11], [x11])
|
||||
PKG_CHECK_MODULES([xinerama], [xinerama])
|
||||
PKG_CHECK_MODULES([pango], [pango pangoxft])
|
||||
PKG_CHECK_MODULES([pango], [pango pangocairo])
|
||||
PKG_CHECK_MODULES([cairo], [cairo cairo-xlib])
|
||||
PKG_CHECK_MODULES([libsn], [libstartup-notification-1.0])
|
||||
|
||||
AC_SUBST([EXTRA_CFLAGS], ["-Wall -Wextra -Wparentheses -Winline -pedantic"])
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#define ROFI_MAIN_H
|
||||
#include <X11/X.h>
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <textbox.h>
|
||||
#include "keyb.h"
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
#ifndef ROFI_SCROLLBAR_H
|
||||
#define ROFI_SCROLLBAR_H
|
||||
|
||||
#include <cairo.h>
|
||||
/**
|
||||
* Internal structure for the scrollbar.
|
||||
*/
|
||||
typedef struct _scrollbar
|
||||
{
|
||||
Window window, parent;
|
||||
short x, y, w, h;
|
||||
GC gc;
|
||||
unsigned int length;
|
||||
unsigned int pos;
|
||||
unsigned int pos_length;
|
||||
|
@ -27,22 +25,7 @@ typedef struct _scrollbar
|
|||
*
|
||||
* @returns the scrollbar object.
|
||||
*/
|
||||
scrollbar *scrollbar_create ( Window parent, XVisualInfo *vinfo, Colormap map,
|
||||
short x, short y, short w, short h );
|
||||
|
||||
/**
|
||||
* @param sb scrollbar object
|
||||
*
|
||||
* Hide (unmap) the scrollbar.
|
||||
*/
|
||||
void scrollbar_hide ( scrollbar *sb );
|
||||
|
||||
/**
|
||||
* @param sb scrollbar object
|
||||
*
|
||||
* Show (map) the scrollbar.
|
||||
*/
|
||||
void scrollbar_show ( scrollbar *sb );
|
||||
scrollbar *scrollbar_create ( short x, short y, short w, short h );
|
||||
|
||||
/**
|
||||
* @param sb scrollbar object
|
||||
|
@ -80,7 +63,7 @@ void scrollbar_set_max_value ( scrollbar *sb, unsigned int max );
|
|||
*
|
||||
* Draw the scrollbar, used after expose event or update
|
||||
*/
|
||||
void scrollbar_draw ( scrollbar *sb );
|
||||
void scrollbar_draw ( scrollbar *sb, cairo_t *draw );
|
||||
|
||||
/**
|
||||
* @param sb scrollbar object
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
#ifndef ROFI_TEXTBOX_H
|
||||
#define ROFI_TEXTBOX_H
|
||||
|
||||
#include <X11/Xutil.h>
|
||||
#include <pango/pango.h>
|
||||
#include <pango/pangoxft.h>
|
||||
#include <pango/pango-fontmap.h>
|
||||
#include <cairo.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double red, green, blue, alpha;
|
||||
} Color;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long flags;
|
||||
Window window, parent;
|
||||
short x, y, w, h;
|
||||
short cursor;
|
||||
XftColor color_fg, color_bg;
|
||||
Color color_fg, color_bg;
|
||||
char *text;
|
||||
XIM xim;
|
||||
XIC xic;
|
||||
|
@ -19,6 +24,11 @@ typedef struct
|
|||
int tbft;
|
||||
int markup;
|
||||
int changed;
|
||||
|
||||
cairo_surface_t *main_surface;
|
||||
cairo_t *main_draw;
|
||||
|
||||
int update;
|
||||
} textbox;
|
||||
|
||||
typedef enum
|
||||
|
@ -49,10 +59,7 @@ typedef enum
|
|||
STATE_MASK = ~( ALT | HIGHLIGHT )
|
||||
} TextBoxFontType;
|
||||
|
||||
textbox* textbox_create ( Window parent,
|
||||
XVisualInfo *vinfo,
|
||||
Colormap map,
|
||||
TextboxFlags flags,
|
||||
textbox* textbox_create ( TextboxFlags flags,
|
||||
short x, short y, short w, short h,
|
||||
TextBoxFontType tbft,
|
||||
const char *text );
|
||||
|
@ -79,19 +86,12 @@ void textbox_font ( textbox *tb, TextBoxFontType tbft );
|
|||
*/
|
||||
void textbox_text ( textbox *tb, const char *text );
|
||||
|
||||
/**
|
||||
* @param tb Handle to the textbox
|
||||
*
|
||||
* Show the textbox (map window)
|
||||
*/
|
||||
void textbox_show ( textbox *tb );
|
||||
|
||||
/**
|
||||
* @param tb Handle to the textbox
|
||||
*
|
||||
* Render the textbox.
|
||||
*/
|
||||
void textbox_draw ( textbox * tb );
|
||||
void textbox_draw ( textbox *tb, cairo_t *draw );
|
||||
|
||||
/**
|
||||
* @param tb Handle to the textbox
|
||||
|
@ -101,7 +101,7 @@ void textbox_draw ( textbox * tb );
|
|||
*
|
||||
* @returns if the key was handled (1), unhandled(0) or handled and return was pressed (-1)
|
||||
*/
|
||||
int textbox_keypress ( textbox *tb, XEvent *ev );
|
||||
int textbox_keypress ( textbox *tb, XIC xic, XEvent *ev );
|
||||
|
||||
/**
|
||||
* @param tb Handle to the textbox
|
||||
|
@ -137,21 +137,11 @@ void textbox_move ( textbox *tb, int x, int y );
|
|||
void textbox_insert ( textbox *tb, int pos, char *str );
|
||||
|
||||
/**
|
||||
* @param tb Handle to the textbox
|
||||
*
|
||||
* Unmap the textbox window. Effectively hiding it.
|
||||
*/
|
||||
void textbox_hide ( textbox *tb );
|
||||
|
||||
/**
|
||||
* @param visual Information about the visual to target
|
||||
* @param colormap The colormap to set the colors for.
|
||||
*
|
||||
* Setup the cached fonts. This is required to do
|
||||
* before any of the textbox_ functions is called.
|
||||
* Clean with textbox_cleanup()
|
||||
*/
|
||||
void textbox_setup ( XVisualInfo *visual, Colormap colormap );
|
||||
void textbox_setup ( void );
|
||||
|
||||
/**
|
||||
* Cleanup the allocated colors and fonts by textbox_setup().
|
||||
|
|
263
source/rofi.c
263
source/rofi.c
|
@ -44,6 +44,9 @@
|
|||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cairo.h>
|
||||
#include <cairo-xlib.h>
|
||||
|
||||
#define SN_API_NOT_YET_FROZEN
|
||||
#include <libsn/sn.h>
|
||||
|
||||
|
@ -77,7 +80,7 @@ SnLauncheeContext *sncontext = NULL;
|
|||
Display *display = NULL;
|
||||
char *display_str = NULL;
|
||||
Window main_window = None;
|
||||
GC gc = NULL;
|
||||
// GC gc = NULL;
|
||||
Colormap map = None;
|
||||
unsigned int normal_window_mode = FALSE;
|
||||
// Array of switchers.
|
||||
|
@ -87,6 +90,11 @@ unsigned int num_switchers = 0;
|
|||
unsigned int curr_switcher = 0;
|
||||
XVisualInfo vinfo;
|
||||
|
||||
cairo_surface_t *surface = NULL;
|
||||
cairo_t *draw = NULL;
|
||||
XIM xim;
|
||||
XIC xic;
|
||||
|
||||
/**
|
||||
* @param name Name of the switcher to lookup.
|
||||
*
|
||||
|
@ -188,44 +196,6 @@ static int levenshtein ( const char *s, const char *t )
|
|||
return dist ( s, t, d, ls, lt, 0, 0 );
|
||||
}
|
||||
|
||||
static Window create_window ( Display *display )
|
||||
{
|
||||
XSetWindowAttributes attr;
|
||||
attr.colormap = map;
|
||||
attr.border_pixel = color_border ( display );
|
||||
attr.background_pixel = color_background ( display );
|
||||
|
||||
Window box = XCreateWindow ( display, DefaultRootWindow ( display ), 0, 0, 200, 100, config.menu_bw, vinfo.depth, InputOutput,
|
||||
vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr );
|
||||
XSelectInput ( display, box, KeyReleaseMask | KeyPressMask | ExposureMask | ButtonPressMask | StructureNotifyMask | FocusChangeMask );
|
||||
|
||||
gc = XCreateGC ( display, box, 0, 0 );
|
||||
int line_style = LineOnOffDash;
|
||||
if ( strcasecmp ( config.separator_style, "dash" ) == 0 ) {
|
||||
line_style = LineOnOffDash;
|
||||
}
|
||||
else if ( strcasecmp ( config.separator_style, "solid" ) == 0 ) {
|
||||
line_style = LineSolid;
|
||||
}
|
||||
XSetLineAttributes ( display, gc, 2, line_style, CapButt, JoinMiter );
|
||||
XSetForeground ( display, gc, color_separator ( display ) );
|
||||
// make it an unmanaged window
|
||||
if ( !normal_window_mode ) {
|
||||
window_set_atom_prop ( display, box, netatoms[_NET_WM_STATE], &netatoms[_NET_WM_STATE_ABOVE], 1 );
|
||||
XSetWindowAttributes sattr = { .override_redirect = True };
|
||||
XChangeWindowAttributes ( display, box, CWOverrideRedirect, &sattr );
|
||||
}
|
||||
else{
|
||||
window_set_atom_prop ( display, box, netatoms[_NET_WM_WINDOW_TYPE], &netatoms[_NET_WM_WINDOW_TYPE_NORMAL], 1 );
|
||||
}
|
||||
|
||||
// Set the WM_NAME
|
||||
XStoreName ( display, box, "rofi" );
|
||||
|
||||
x11_set_window_opacity ( display, box, config.window_opacity );
|
||||
return box;
|
||||
}
|
||||
|
||||
// State of the menu.
|
||||
|
||||
typedef struct MenuState
|
||||
|
@ -278,6 +248,47 @@ typedef struct MenuState
|
|||
int line_height;
|
||||
}MenuState;
|
||||
|
||||
static Window create_window ( Display *display )
|
||||
{
|
||||
XSetWindowAttributes attr;
|
||||
attr.colormap = map;
|
||||
attr.border_pixel = color_border ( display );
|
||||
attr.background_pixel = color_background ( display );
|
||||
|
||||
Window box = XCreateWindow ( display, DefaultRootWindow ( display ), 0, 0, 200, 100, config.menu_bw, vinfo.depth, InputOutput,
|
||||
vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr );
|
||||
XSelectInput (
|
||||
display,
|
||||
box,
|
||||
KeyReleaseMask | KeyPressMask | ExposureMask | ButtonPressMask | StructureNotifyMask | FocusChangeMask |
|
||||
Button1MotionMask );
|
||||
|
||||
surface = cairo_xlib_surface_create ( display, box, vinfo.visual, 200, 100 );
|
||||
// Create a drawable.
|
||||
draw = cairo_create ( surface );
|
||||
cairo_set_operator ( draw, CAIRO_OPERATOR_SOURCE );
|
||||
|
||||
// // make it an unmanaged window
|
||||
if ( !normal_window_mode ) {
|
||||
window_set_atom_prop ( display, box, netatoms[_NET_WM_STATE], &netatoms[_NET_WM_STATE_ABOVE], 1 );
|
||||
XSetWindowAttributes sattr = { .override_redirect = True };
|
||||
XChangeWindowAttributes ( display, box, CWOverrideRedirect, &sattr );
|
||||
}
|
||||
else{
|
||||
window_set_atom_prop ( display, box, netatoms[_NET_WM_WINDOW_TYPE], &netatoms[_NET_WM_WINDOW_TYPE_NORMAL], 1 );
|
||||
}
|
||||
|
||||
xim = XOpenIM ( display, NULL, NULL, NULL );
|
||||
xic = XCreateIC ( xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow,
|
||||
box, XNFocusWindow, box, NULL );
|
||||
|
||||
// Set the WM_NAME
|
||||
XStoreName ( display, box, "rofi" );
|
||||
|
||||
x11_set_window_opacity ( display, box, config.window_opacity );
|
||||
return box;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param state Internal state of the menu.
|
||||
*
|
||||
|
@ -632,6 +643,26 @@ static void menu_keyboard_navigation ( MenuState *state, KeySym key, unsigned in
|
|||
* mouse navigation through the elements.
|
||||
*
|
||||
*/
|
||||
static int intersect ( textbox *tb, int x, int y )
|
||||
{
|
||||
if ( x >= ( tb->x ) && x < ( tb->x + tb->w ) ) {
|
||||
if ( y >= ( tb->y ) && y < ( tb->y + tb->h ) ) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
static int sb_intersect ( scrollbar *tb, int x, int y )
|
||||
{
|
||||
if ( x >= ( tb->x ) && x < ( tb->x + tb->w ) ) {
|
||||
if ( y >= ( tb->y ) && y < ( tb->y + tb->h ) ) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
static void menu_mouse_navigation ( MenuState *state, XButtonEvent *xbe )
|
||||
{
|
||||
// Scroll event
|
||||
|
@ -651,13 +682,13 @@ static void menu_mouse_navigation ( MenuState *state, XButtonEvent *xbe )
|
|||
return;
|
||||
}
|
||||
else {
|
||||
if ( state->scrollbar && state->scrollbar->window == xbe->window ) {
|
||||
if ( state->scrollbar && sb_intersect ( state->scrollbar, xbe->x, xbe->y ) ) {
|
||||
state->selected = scrollbar_clicked ( state->scrollbar, xbe->y );
|
||||
state->update = TRUE;
|
||||
return;
|
||||
}
|
||||
for ( unsigned int i = 0; config.sidebar_mode == TRUE && i < num_switchers; i++ ) {
|
||||
if ( switchers[i].tb->window == ( xbe->window ) ) {
|
||||
if ( intersect ( switchers[i].tb, xbe->x, xbe->y ) ) {
|
||||
*( state->selected_line ) = 0;
|
||||
state->retv = MENU_QUICK_SWITCH | ( i & MENU_LOWER_MASK );
|
||||
state->quit = TRUE;
|
||||
|
@ -666,7 +697,7 @@ static void menu_mouse_navigation ( MenuState *state, XButtonEvent *xbe )
|
|||
}
|
||||
}
|
||||
for ( unsigned int i = 0; i < state->max_elements; i++ ) {
|
||||
if ( ( xbe->window ) == ( state->boxes[i]->window ) ) {
|
||||
if ( intersect ( state->boxes[i], xbe->x, xbe->y ) ) {
|
||||
// Only allow items that are visible to be selected.
|
||||
if ( ( state->last_offset + i ) >= state->filtered_lines ) {
|
||||
break;
|
||||
|
@ -734,10 +765,20 @@ static void menu_refilter ( MenuState *state )
|
|||
state->rchanged = TRUE;
|
||||
}
|
||||
|
||||
static void menu_draw ( MenuState *state )
|
||||
static void menu_draw ( MenuState *state, cairo_t *draw )
|
||||
{
|
||||
unsigned int i, offset = 0;
|
||||
|
||||
unsigned pixel = color_background ( display );
|
||||
|
||||
cairo_set_source_rgba ( draw,
|
||||
( ( pixel & 0x00FF0000 ) >> 16 ) / 256.0,
|
||||
( ( pixel & 0x0000FF00 ) >> 8 ) / 256.0,
|
||||
( ( pixel & 0x000000FF ) >> 0 ) / 256.0,
|
||||
( ( pixel & 0xFF000000 ) >> 24 ) / 256.0
|
||||
);
|
||||
cairo_paint ( draw );
|
||||
|
||||
// selected row is always visible.
|
||||
// If selected is visible do not scroll.
|
||||
if ( ( ( state->selected - ( state->last_offset ) ) < ( state->max_elements ) ) && ( state->selected >= ( state->last_offset ) ) ) {
|
||||
|
@ -752,7 +793,7 @@ static void menu_draw ( MenuState *state )
|
|||
state->cur_page = page;
|
||||
state->rchanged = TRUE;
|
||||
}
|
||||
// Set the position.
|
||||
// Set the position
|
||||
scrollbar_set_handle ( state->scrollbar, page * state->max_elements );
|
||||
}
|
||||
// Re calculate the boxes and sizes, see if we can move this in the menu_calc*rowscolumns
|
||||
|
@ -765,7 +806,7 @@ static void menu_draw ( MenuState *state )
|
|||
|
||||
// Update the handle length.
|
||||
scrollbar_set_handle_length ( state->scrollbar, columns * state->max_rows );
|
||||
scrollbar_draw ( state->scrollbar );
|
||||
scrollbar_draw ( state->scrollbar, draw );
|
||||
// Element width.
|
||||
unsigned int element_width = state->w - ( 2 * ( config.padding ) );
|
||||
if ( state->scrollbar != NULL ) {
|
||||
|
@ -781,10 +822,6 @@ static void menu_draw ( MenuState *state )
|
|||
// Calculate number of visible rows.
|
||||
unsigned int max_elements = MIN ( a_lines, state->max_rows * columns );
|
||||
|
||||
// Hide now invisible boxes.
|
||||
for ( i = max_elements; i < state->max_elements; i++ ) {
|
||||
textbox_hide ( state->boxes[i] );
|
||||
}
|
||||
if ( state->rchanged ) {
|
||||
// Move, resize visible boxes and show them.
|
||||
for ( i = 0; i < max_elements; i++ ) {
|
||||
|
@ -800,8 +837,7 @@ static void menu_draw ( MenuState *state )
|
|||
textbox_font ( state->boxes[i], tbft );
|
||||
textbox_text ( state->boxes[i], text );
|
||||
}
|
||||
textbox_show ( state->boxes[i] );
|
||||
textbox_draw ( state->boxes[i] );
|
||||
textbox_draw ( state->boxes[i], draw );
|
||||
}
|
||||
state->rchanged = FALSE;
|
||||
}
|
||||
|
@ -813,37 +849,63 @@ static void menu_draw ( MenuState *state )
|
|||
state->sw->mgrv ( state->line_map[i + offset], state->sw, &fstate );
|
||||
TextBoxFontType tbft = fstate | ( ( i + offset ) == state->selected ? HIGHLIGHT : type );
|
||||
textbox_font ( state->boxes[i], tbft );
|
||||
textbox_draw ( state->boxes[i] );
|
||||
textbox_draw ( state->boxes[i], draw );
|
||||
}
|
||||
}
|
||||
cairo_show_page ( draw );
|
||||
}
|
||||
|
||||
static void menu_update ( MenuState *state )
|
||||
{
|
||||
textbox_draw ( state->case_indicator );
|
||||
textbox_draw ( state->prompt_tb );
|
||||
textbox_draw ( state->text );
|
||||
cairo_surface_t *surf = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, state->w, state->h );
|
||||
cairo_t *d = cairo_create ( surf );
|
||||
cairo_set_operator ( d, CAIRO_OPERATOR_SOURCE );
|
||||
|
||||
menu_draw ( state, d );
|
||||
textbox_draw ( state->prompt_tb, d );
|
||||
textbox_draw ( state->text, d );
|
||||
textbox_draw ( state->case_indicator, d );
|
||||
if ( state->message_tb ) {
|
||||
textbox_draw ( state->message_tb );
|
||||
textbox_draw ( state->message_tb, d );
|
||||
}
|
||||
menu_draw ( state );
|
||||
// Why do we need the special -1?
|
||||
XDrawLine ( display, main_window, gc, 0, state->line_height + ( config.padding ) * 1 + config.line_margin + 1, state->w,
|
||||
state->line_height + ( config.padding ) * 1 + config.line_margin + 1 );
|
||||
unsigned pixel = color_separator ( display );
|
||||
|
||||
cairo_set_source_rgba ( d,
|
||||
( ( pixel & 0x00FF0000 ) >> 16 ) / 256.0,
|
||||
( ( pixel & 0x0000FF00 ) >> 8 ) / 256.0,
|
||||
( ( pixel & 0x000000FF ) >> 0 ) / 256.0,
|
||||
( ( pixel & 0xFF000000 ) >> 24 ) / 256.0
|
||||
);
|
||||
if ( strcmp ( config.separator_style, "dash" ) == 0 ) {
|
||||
const double dashes[1] = { 4 };
|
||||
cairo_set_dash ( d, dashes, 1, 0.0 );
|
||||
}
|
||||
cairo_move_to ( d, 0, state->line_height + ( config.padding ) * 1 + config.line_margin + 1 );
|
||||
cairo_line_to ( d, state->w, state->line_height + ( config.padding ) * 1 + config.line_margin + 1 );
|
||||
cairo_stroke ( d );
|
||||
if ( state->message_tb ) {
|
||||
XDrawLine ( display, main_window, gc, 0, state->top_offset - ( config.line_margin ) - 1,
|
||||
state->w, state->top_offset - ( config.line_margin ) - 1 );
|
||||
cairo_move_to ( d, 0, state->top_offset - ( config.line_margin ) - 1 );
|
||||
cairo_line_to ( d, state->w, state->top_offset - ( config.line_margin ) - 1 );
|
||||
cairo_stroke ( d );
|
||||
}
|
||||
|
||||
if ( config.sidebar_mode == TRUE ) {
|
||||
XDrawLine ( display, main_window, gc, 0, state->h - state->line_height - ( config.padding ) * 1 - 1 - config.line_margin,
|
||||
state->w, state->h - state->line_height - ( config.padding ) * 1 - 1 - config.line_margin );
|
||||
cairo_move_to ( d, 0, state->h - state->line_height - ( config.padding ) * 1 - 1 - config.line_margin );
|
||||
cairo_line_to ( d, state->w, state->h - state->line_height - ( config.padding ) * 1 - 1 - config.line_margin );
|
||||
cairo_stroke ( d );
|
||||
|
||||
for ( unsigned int j = 0; j < num_switchers; j++ ) {
|
||||
textbox_draw ( switchers[j].tb );
|
||||
textbox_draw ( switchers[j].tb, d );
|
||||
}
|
||||
}
|
||||
|
||||
state->update = FALSE;
|
||||
|
||||
cairo_set_source_surface ( draw, surf, 0, 0 );
|
||||
cairo_paint ( draw );
|
||||
|
||||
cairo_destroy ( d );
|
||||
cairo_surface_destroy ( surf );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -883,8 +945,7 @@ static void menu_resize ( MenuState *state )
|
|||
for ( unsigned int j = 0; j < num_switchers; j++ ) {
|
||||
textbox_moveresize ( switchers[j].tb, config.padding + j * ( width + config.line_margin ),
|
||||
state->h - state->line_height - config.padding, width, state->line_height );
|
||||
textbox_show ( switchers[j].tb );
|
||||
textbox_draw ( switchers[j].tb );
|
||||
textbox_draw ( switchers[j].tb, draw );
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
@ -915,7 +976,7 @@ static void menu_resize ( MenuState *state )
|
|||
}
|
||||
// Add newly added boxes.
|
||||
for ( unsigned int i = last_length; i < state->max_elements; i++ ) {
|
||||
state->boxes[i] = textbox_create ( main_window, &vinfo, map, rstate, x_offset, y_offset,
|
||||
state->boxes[i] = textbox_create ( rstate, x_offset, y_offset,
|
||||
state->element_width, element_height, NORMAL, "" );
|
||||
}
|
||||
scrollbar_resize ( state->scrollbar, -1, ( state->max_rows ) * ( element_height ) - config.line_margin );
|
||||
|
@ -977,7 +1038,7 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
|
|||
|
||||
// we need this at this point so we can get height.
|
||||
state.line_height = textbox_get_estimated_char_height ();
|
||||
state.case_indicator = textbox_create ( main_window, &vinfo, map, TB_AUTOWIDTH, ( config.padding ), ( config.padding ),
|
||||
state.case_indicator = textbox_create ( TB_AUTOWIDTH, ( config.padding ), ( config.padding ),
|
||||
0, state.line_height, NORMAL, "*" );
|
||||
// Height of a row.
|
||||
if ( config.menu_lines == 0 ) {
|
||||
|
@ -993,13 +1054,13 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
|
|||
menu_calculate_window_and_element_width ( &state, &mon );
|
||||
|
||||
// Prompt box.
|
||||
state.prompt_tb = textbox_create ( main_window, &vinfo, map, TB_AUTOWIDTH, ( config.padding ), ( config.padding ),
|
||||
state.prompt_tb = textbox_create ( TB_AUTOWIDTH, ( config.padding ), ( config.padding ),
|
||||
0, state.line_height, NORMAL, prompt );
|
||||
// Entry box
|
||||
int entrybox_width = state.w - ( 2 * ( config.padding ) ) - textbox_get_width ( state.prompt_tb )
|
||||
- textbox_get_width ( state.case_indicator );
|
||||
|
||||
state.text = textbox_create ( main_window, &vinfo, map, TB_EDITABLE,
|
||||
state.text = textbox_create ( TB_EDITABLE,
|
||||
( config.padding ) + textbox_get_width ( state.prompt_tb ), ( config.padding ),
|
||||
entrybox_width, state.line_height, NORMAL, *input );
|
||||
|
||||
|
@ -1008,19 +1069,17 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
|
|||
// Move indicator to end.
|
||||
textbox_move ( state.case_indicator, config.padding + textbox_get_width ( state.prompt_tb ) + entrybox_width, config.padding );
|
||||
|
||||
textbox_show ( state.text );
|
||||
textbox_show ( state.prompt_tb );
|
||||
|
||||
if ( config.case_sensitive ) {
|
||||
textbox_show ( state.case_indicator );
|
||||
textbox_text ( state.case_indicator, "*" );
|
||||
}
|
||||
else{
|
||||
textbox_text ( state.case_indicator, "" );
|
||||
}
|
||||
|
||||
state.message_tb = NULL;
|
||||
if ( message ) {
|
||||
state.message_tb = textbox_create ( main_window, &vinfo, map, TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP,
|
||||
state.message_tb = textbox_create ( TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP,
|
||||
( config.padding ), state.top_offset, state.w - ( 2 * ( config.padding ) ),
|
||||
-1, NORMAL, message );
|
||||
textbox_show ( state.message_tb );
|
||||
state.top_offset += textbox_get_height ( state.message_tb );
|
||||
state.top_offset += config.line_margin * 2 + 2;
|
||||
}
|
||||
|
@ -1037,12 +1096,12 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
|
|||
rstate = TB_MARKUP;
|
||||
}
|
||||
for ( unsigned int i = 0; i < state.max_elements; i++ ) {
|
||||
state.boxes[i] = textbox_create ( main_window, &vinfo, map, rstate, x_offset, y_offset,
|
||||
state.boxes[i] = textbox_create ( rstate, x_offset, y_offset,
|
||||
state.element_width, element_height, NORMAL, "" );
|
||||
}
|
||||
if ( !config.hide_scrollbar ) {
|
||||
unsigned int sbw = config.line_margin + 8;
|
||||
state.scrollbar = scrollbar_create ( main_window, &vinfo, map, state.w - config.padding - sbw, state.top_offset,
|
||||
state.scrollbar = scrollbar_create ( state.w - config.padding - sbw, state.top_offset,
|
||||
sbw, ( state.max_rows - 1 ) * ( element_height + config.line_margin ) + element_height );
|
||||
}
|
||||
|
||||
|
@ -1074,22 +1133,22 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
|
|||
if ( config.sidebar_mode == TRUE ) {
|
||||
int width = ( state.w - ( 2 * ( config.padding ) + ( num_switchers - 1 ) * config.line_margin ) ) / num_switchers;
|
||||
for ( unsigned int j = 0; j < num_switchers; j++ ) {
|
||||
switchers[j].tb = textbox_create ( main_window, &vinfo, map, TB_CENTER, config.padding + j * ( width + config.line_margin ),
|
||||
switchers[j].tb = textbox_create ( TB_CENTER, config.padding + j * ( width + config.line_margin ),
|
||||
state.h - state.line_height - config.padding, width, state.line_height,
|
||||
( j == curr_switcher ) ? HIGHLIGHT : NORMAL, switchers[j].sw->name );
|
||||
textbox_show ( switchers[j].tb );
|
||||
}
|
||||
}
|
||||
|
||||
scrollbar_show ( state.scrollbar );
|
||||
// Display it.
|
||||
XMoveResizeWindow ( display, main_window, state.x, state.y, state.w, state.h );
|
||||
cairo_xlib_surface_set_size ( surface, state.w, state.h );
|
||||
XMapRaised ( display, main_window );
|
||||
|
||||
// if grabbing keyboard failed, fall through
|
||||
state.selected = 0;
|
||||
|
||||
state.quit = FALSE;
|
||||
state.update = TRUE;
|
||||
menu_refilter ( &state );
|
||||
|
||||
for ( unsigned int i = 0; ( *( state.selected_line ) ) < UINT32_MAX && !state.selected && i < state.filtered_lines; i++ ) {
|
||||
|
@ -1151,6 +1210,7 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
|
|||
if ( state.w != (unsigned int) xce.width || state.h != (unsigned int ) xce.height ) {
|
||||
state.w = xce.width;
|
||||
state.h = xce.height;
|
||||
cairo_xlib_surface_set_size ( surface, state.w, state.h );
|
||||
menu_resize ( &state );
|
||||
}
|
||||
}
|
||||
|
@ -1173,9 +1233,11 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
|
|||
;
|
||||
}
|
||||
XMotionEvent xme = ev.xmotion;
|
||||
if ( xme.x >= state.scrollbar->x && xme.x < ( state.scrollbar->x + state.scrollbar->w ) ) {
|
||||
state.selected = scrollbar_clicked ( state.scrollbar, xme.y );
|
||||
state.update = TRUE;
|
||||
}
|
||||
}
|
||||
// Button press event.
|
||||
else if ( ev.type == ButtonPress ) {
|
||||
while ( XCheckTypedEvent ( display, ButtonPress, &ev ) ) {
|
||||
|
@ -1222,10 +1284,10 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
|
|||
state.refilter = TRUE;
|
||||
state.update = TRUE;
|
||||
if ( config.case_sensitive ) {
|
||||
textbox_show ( state.case_indicator );
|
||||
textbox_text ( state.case_indicator, "*" );
|
||||
}
|
||||
else {
|
||||
textbox_hide ( state.case_indicator );
|
||||
textbox_text ( state.case_indicator, "" );
|
||||
}
|
||||
}
|
||||
// Special delete entry command.
|
||||
|
@ -1253,7 +1315,7 @@ MenuReturn menu ( Switcher *sw, char **input, char *prompt, unsigned int *select
|
|||
continue;
|
||||
}
|
||||
|
||||
int rc = textbox_keypress ( state.text, &ev );
|
||||
int rc = textbox_keypress ( state.text, xic, &ev );
|
||||
// Row is accepted.
|
||||
if ( rc < 0 ) {
|
||||
shift = ( ( ev.xkey.state & ShiftMask ) == ShiftMask );
|
||||
|
@ -1370,10 +1432,9 @@ void error_dialog ( const char *msg, int markup )
|
|||
menu_calculate_window_and_element_width ( &state, &mon );
|
||||
state.max_elements = 0;
|
||||
|
||||
state.text = textbox_create ( main_window, &vinfo, map, TB_AUTOHEIGHT|TB_WRAP + ( ( markup ) ? TB_MARKUP : 0 ),
|
||||
state.text = textbox_create ( TB_AUTOHEIGHT | TB_WRAP + ( ( markup ) ? TB_MARKUP : 0 ),
|
||||
( config.padding ), ( config.padding ),
|
||||
( state.w - ( 2 * ( config.padding ) ) ), 1, NORMAL, ( msg != NULL ) ? msg : "" );
|
||||
textbox_show ( state.text );
|
||||
state.line_height = textbox_get_height ( state.text );
|
||||
|
||||
// resize window vertically to suit
|
||||
|
@ -1382,7 +1443,7 @@ void error_dialog ( const char *msg, int markup )
|
|||
// Move the window to the correct x,y position.
|
||||
calculate_window_position ( &state, &mon );
|
||||
XMoveResizeWindow ( display, main_window, state.x, state.y, state.w, state.h );
|
||||
|
||||
cairo_xlib_surface_set_size ( surface, state.w, state.h );
|
||||
// Display it.
|
||||
XMapRaised ( display, main_window );
|
||||
|
||||
|
@ -1392,7 +1453,7 @@ void error_dialog ( const char *msg, int markup )
|
|||
while ( !state.quit ) {
|
||||
// Update if requested.
|
||||
if ( state.update ) {
|
||||
textbox_draw ( state.text );
|
||||
textbox_draw ( state.text, draw );
|
||||
state.update = FALSE;
|
||||
}
|
||||
// Wait for event.
|
||||
|
@ -1430,7 +1491,7 @@ static int setup ()
|
|||
if ( pfd >= 0 ) {
|
||||
// Request truecolor visual.
|
||||
create_visual_and_colormap ( display );
|
||||
textbox_setup ( &vinfo, map );
|
||||
textbox_setup ( );
|
||||
}
|
||||
return pfd;
|
||||
}
|
||||
|
@ -1448,11 +1509,18 @@ static void teardown ( int pfd )
|
|||
XUnmapWindow ( display, main_window );
|
||||
XDestroyWindow ( display, main_window );
|
||||
main_window = None;
|
||||
XDestroyIC ( xic );
|
||||
XCloseIM ( xim );
|
||||
}
|
||||
if ( gc != NULL ) {
|
||||
XFreeGC ( display, gc );
|
||||
gc = NULL;
|
||||
if ( draw ) {
|
||||
cairo_destroy ( draw );
|
||||
draw = NULL;
|
||||
}
|
||||
if ( surface ) {
|
||||
cairo_surface_destroy ( surface );
|
||||
surface = NULL;
|
||||
}
|
||||
|
||||
if ( map != None ) {
|
||||
XFreeColormap ( display, map );
|
||||
map = None;
|
||||
|
@ -1580,11 +1648,10 @@ static void cleanup ()
|
|||
release_keyboard ( display );
|
||||
XDestroyWindow ( display, main_window );
|
||||
main_window = None;
|
||||
XDestroyIC ( xic );
|
||||
XCloseIM ( xim );
|
||||
}
|
||||
if ( gc != NULL ) {
|
||||
XFreeGC ( display, gc );
|
||||
gc = NULL;
|
||||
}
|
||||
|
||||
if ( sncontext != NULL ) {
|
||||
sn_launchee_context_unref ( sncontext );
|
||||
sncontext = NULL;
|
||||
|
|
|
@ -37,11 +37,10 @@
|
|||
|
||||
extern Display *display;
|
||||
|
||||
scrollbar *scrollbar_create ( Window parent, XVisualInfo *vinfo, Colormap map, short x, short y, short w, short h )
|
||||
scrollbar *scrollbar_create ( short x, short y, short w, short h )
|
||||
{
|
||||
scrollbar *sb = g_malloc0 ( sizeof ( scrollbar ) );
|
||||
|
||||
sb->parent = parent;
|
||||
sb->x = x;
|
||||
sb->y = y;
|
||||
sb->w = MAX ( 1, w );
|
||||
|
@ -51,39 +50,13 @@ scrollbar *scrollbar_create ( Window parent, XVisualInfo *vinfo, Colormap map, s
|
|||
sb->pos = 0;
|
||||
sb->pos_length = 4;
|
||||
|
||||
XSetWindowAttributes attr;
|
||||
attr.colormap = map;
|
||||
attr.border_pixel = color_border ( display );
|
||||
attr.background_pixel = color_background ( display );
|
||||
sb->window = XCreateWindow ( display, sb->parent, sb->x, sb->y, sb->w, sb->h, 0, vinfo->depth, InputOutput, vinfo->visual,
|
||||
CWColormap | CWBorderPixel | CWBackPixel, &attr );
|
||||
|
||||
XSelectInput ( display, sb->window, ExposureMask | ButtonPressMask | Button1MotionMask );
|
||||
sb->gc = XCreateGC ( display, sb->window, 0, 0 );
|
||||
XSetForeground ( display, sb->gc, color_separator ( display ) );
|
||||
|
||||
// Create GC.
|
||||
return sb;
|
||||
}
|
||||
|
||||
void scrollbar_show ( scrollbar *sb )
|
||||
{
|
||||
if ( sb != NULL ) {
|
||||
XMapWindow ( display, sb->window );
|
||||
}
|
||||
}
|
||||
void scrollbar_hide ( scrollbar *sb )
|
||||
{
|
||||
if ( sb != NULL ) {
|
||||
XUnmapWindow ( display, sb->window );
|
||||
}
|
||||
}
|
||||
|
||||
void scrollbar_free ( scrollbar *sb )
|
||||
{
|
||||
if ( sb != NULL ) {
|
||||
XFreeGC ( display, sb->gc );
|
||||
XDestroyWindow ( display, sb->window );
|
||||
g_free ( sb );
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +82,7 @@ void scrollbar_set_handle_length ( scrollbar *sb, unsigned int pos_length )
|
|||
}
|
||||
}
|
||||
|
||||
void scrollbar_draw ( scrollbar *sb )
|
||||
void scrollbar_draw ( scrollbar *sb, cairo_t *draw )
|
||||
{
|
||||
if ( sb != NULL ) {
|
||||
// Calculate position and size.
|
||||
|
@ -124,9 +97,16 @@ void scrollbar_draw ( scrollbar *sb )
|
|||
// Cap length;
|
||||
height = MIN ( bh - y + 1, ( height ) );
|
||||
// Redraw base window
|
||||
XClearWindow ( display, sb->window );
|
||||
// Paint the handle.
|
||||
XFillRectangle ( display, sb->window, sb->gc, config.line_margin, y, sb->w - config.line_margin, height );
|
||||
unsigned pixel = color_separator ( display );
|
||||
|
||||
cairo_set_source_rgba ( draw,
|
||||
( ( pixel & 0x00FF0000 ) >> 16 ) / 256.0,
|
||||
( ( pixel & 0x0000FF00 ) >> 8 ) / 256.0,
|
||||
( ( pixel & 0x000000FF ) >> 0 ) / 256.0,
|
||||
( ( pixel & 0xFF000000 ) >> 24 ) / 256.0
|
||||
);
|
||||
cairo_rectangle ( draw, sb->x + config.line_margin, sb->y + y, sb->w - config.line_margin, height );
|
||||
cairo_fill ( draw );
|
||||
}
|
||||
}
|
||||
void scrollbar_resize ( scrollbar *sb, int w, int h )
|
||||
|
@ -138,7 +118,6 @@ void scrollbar_resize ( scrollbar *sb, int w, int h )
|
|||
if ( w > 0 ) {
|
||||
sb->w = w;
|
||||
}
|
||||
XResizeWindow ( display, sb->window, sb->w, sb->h );
|
||||
}
|
||||
}
|
||||
void scrollbar_move ( scrollbar *sb, int x, int y )
|
||||
|
@ -146,18 +125,20 @@ void scrollbar_move ( scrollbar *sb, int x, int y )
|
|||
if ( sb != NULL ) {
|
||||
sb->x = x;
|
||||
sb->y = y;
|
||||
XMoveWindow ( display, sb->window, x, y );
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int scrollbar_clicked ( scrollbar *sb, int y )
|
||||
{
|
||||
if ( sb != NULL ) {
|
||||
if ( y >= sb->y && y < ( sb->y + sb->h ) ) {
|
||||
y -= sb->y;
|
||||
y = MIN ( MAX ( 1, y ), sb->h - 1 ) - 1;
|
||||
const short bh = sb->h - 2;
|
||||
float sec = ( ( bh ) / (float) sb->length );
|
||||
unsigned int sel = y / sec;
|
||||
return MIN ( sel, sb->length - 1 );
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
278
source/textbox.c
278
source/textbox.c
|
@ -32,9 +32,8 @@
|
|||
#include <X11/Xproto.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/Xft/Xft.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <pango/pangocairo.h>
|
||||
#include "rofi.h"
|
||||
#include "textbox.h"
|
||||
#include "keyb.h"
|
||||
|
@ -47,16 +46,13 @@ extern Display *display;
|
|||
* Font + font color cache.
|
||||
* Avoid re-loading font on every change on every textbox.
|
||||
*/
|
||||
XVisualInfo *visual_info;
|
||||
Colormap target_colormap;
|
||||
|
||||
typedef struct _RowColor
|
||||
{
|
||||
XftColor fg;
|
||||
XftColor bg;
|
||||
XftColor bgalt;
|
||||
XftColor hlfg;
|
||||
XftColor hlbg;
|
||||
Color fg;
|
||||
Color bg;
|
||||
Color bgalt;
|
||||
Color hlfg;
|
||||
Color hlbg;
|
||||
} RowColor;
|
||||
|
||||
#define num_states 3
|
||||
|
@ -64,45 +60,23 @@ RowColor colors[num_states];
|
|||
|
||||
PangoContext *p_context = NULL;
|
||||
|
||||
// Xft text box, optionally editable
|
||||
textbox* textbox_create ( Window parent, XVisualInfo *vinfo, Colormap map, TextboxFlags flags, short x, short y, short w, short h,
|
||||
textbox* textbox_create ( TextboxFlags flags, short x, short y, short w, short h,
|
||||
TextBoxFontType tbft, const char *text )
|
||||
{
|
||||
textbox *tb = g_malloc0 ( sizeof ( textbox ) );
|
||||
|
||||
tb->flags = flags;
|
||||
tb->parent = parent;
|
||||
|
||||
tb->x = x;
|
||||
tb->y = y;
|
||||
tb->w = MAX ( 1, w );
|
||||
tb->h = MAX ( 1, h );
|
||||
|
||||
tb->layout = pango_layout_new ( p_context );
|
||||
|
||||
tb->changed = FALSE;
|
||||
|
||||
unsigned int cp;
|
||||
switch ( tbft )
|
||||
{
|
||||
case HIGHLIGHT:
|
||||
cp = colors[NORMAL].hlbg.pixel;
|
||||
break;
|
||||
case ALT:
|
||||
cp = colors[NORMAL].bgalt.pixel;
|
||||
break;
|
||||
default:
|
||||
cp = colors[NORMAL].bg.pixel;
|
||||
break;
|
||||
}
|
||||
|
||||
XSetWindowAttributes attr;
|
||||
attr.colormap = map;
|
||||
attr.border_pixel = cp;
|
||||
attr.background_pixel = cp;
|
||||
tb->window = XCreateWindow ( display, tb->parent, tb->x, tb->y, tb->w, tb->h, 0, vinfo->depth,
|
||||
InputOutput, vinfo->visual, CWColormap | CWBorderPixel | CWBackPixel, &attr );
|
||||
|
||||
tb->main_surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, tb->w, tb->h );
|
||||
tb->main_draw = cairo_create ( tb->main_surface );
|
||||
tb->layout = pango_cairo_create_layout ( tb->main_draw );
|
||||
PangoFontDescription *pfd = pango_font_description_from_string ( config.menu_font );
|
||||
pango_layout_set_font_description ( tb->layout, pfd );
|
||||
pango_font_description_free ( pfd );
|
||||
|
@ -117,20 +91,9 @@ textbox* textbox_create ( Window parent, XVisualInfo *vinfo, Colormap map, Textb
|
|||
// auto height/width modes get handled here
|
||||
textbox_moveresize ( tb, tb->x, tb->y, tb->w, tb->h );
|
||||
|
||||
// edit mode controls
|
||||
if ( tb->flags & TB_EDITABLE ) {
|
||||
tb->xim = XOpenIM ( display, NULL, NULL, NULL );
|
||||
tb->xic = XCreateIC ( tb->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow,
|
||||
tb->window, XNFocusWindow, tb->window, NULL );
|
||||
}
|
||||
else {
|
||||
XSelectInput ( display, tb->window, ButtonPressMask );
|
||||
}
|
||||
|
||||
return tb;
|
||||
}
|
||||
|
||||
// set an Xft font by name
|
||||
void textbox_font ( textbox *tb, TextBoxFontType tbft )
|
||||
{
|
||||
RowColor *color = &( colors[tbft & STATE_MASK] );
|
||||
|
@ -149,14 +112,19 @@ void textbox_font ( textbox *tb, TextBoxFontType tbft )
|
|||
tb->color_fg = color->fg;
|
||||
break;
|
||||
}
|
||||
if ( tb->tbft != tbft ) {
|
||||
tb->update = TRUE;
|
||||
}
|
||||
tb->tbft = tbft;
|
||||
}
|
||||
|
||||
// set the default text to display
|
||||
void textbox_text ( textbox *tb, const char *text )
|
||||
{
|
||||
tb->update = TRUE;
|
||||
g_free ( tb->text );
|
||||
const gchar *last_pointer = NULL;
|
||||
|
||||
if ( g_utf8_validate ( text, -1, &last_pointer ) ) {
|
||||
tb->text = g_strdup ( text );
|
||||
}
|
||||
|
@ -169,6 +137,7 @@ void textbox_text ( textbox *tb, const char *text )
|
|||
tb->text = g_strdup ( "Invalid UTF-8 string." );
|
||||
}
|
||||
}
|
||||
|
||||
if ( tb->flags & TB_MARKUP ) {
|
||||
pango_layout_set_markup ( tb->layout, tb->text, strlen ( tb->text ) );
|
||||
}
|
||||
|
@ -187,7 +156,6 @@ void textbox_move ( textbox *tb, int x, int y )
|
|||
if ( x != tb->x || y != tb->y ) {
|
||||
tb->x = x;
|
||||
tb->y = y;
|
||||
XMoveResizeWindow ( display, tb->window, tb->x, tb->y, tb->w, tb->h );
|
||||
}
|
||||
}
|
||||
// within the parent handled auto width/height modes
|
||||
|
@ -195,13 +163,8 @@ void textbox_moveresize ( textbox *tb, int x, int y, int w, int h )
|
|||
{
|
||||
if ( tb->flags & TB_AUTOWIDTH ) {
|
||||
pango_layout_set_width ( tb->layout, -1 );
|
||||
if ( w > 1 ) {
|
||||
w = MIN ( w, textbox_get_width ( tb ) );
|
||||
}
|
||||
else{
|
||||
w = textbox_get_width ( tb );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// set ellipsize
|
||||
if ( ( tb->flags & TB_EDITABLE ) == TB_EDITABLE ) {
|
||||
|
@ -224,19 +187,10 @@ void textbox_moveresize ( textbox *tb, int x, int y, int w, int h )
|
|||
tb->y = y;
|
||||
tb->h = MAX ( 1, h );
|
||||
tb->w = MAX ( 1, w );
|
||||
XMoveResizeWindow ( display, tb->window, tb->x, tb->y, tb->w, tb->h );
|
||||
}
|
||||
// We always want to update this
|
||||
pango_layout_set_width ( tb->layout, PANGO_SCALE * ( tb->w - 2 * SIDE_MARGIN ) );
|
||||
}
|
||||
|
||||
void textbox_show ( textbox *tb )
|
||||
{
|
||||
XMapWindow ( display, tb->window );
|
||||
}
|
||||
void textbox_hide ( textbox *tb )
|
||||
{
|
||||
XUnmapWindow ( display, tb->window );
|
||||
// We always want to update this
|
||||
pango_layout_set_width ( tb->layout, PANGO_SCALE * ( tb->w - 2 * SIDE_MARGIN ) );
|
||||
}
|
||||
|
||||
// will also unmap the window if still displayed
|
||||
|
@ -245,30 +199,40 @@ void textbox_free ( textbox *tb )
|
|||
if ( tb == NULL ) {
|
||||
return;
|
||||
}
|
||||
if ( tb->flags & TB_EDITABLE ) {
|
||||
XDestroyIC ( tb->xic );
|
||||
XCloseIM ( tb->xim );
|
||||
}
|
||||
|
||||
g_free ( tb->text );
|
||||
|
||||
if ( tb->layout != NULL ) {
|
||||
g_object_unref ( tb->layout );
|
||||
}
|
||||
if ( tb->main_draw ) {
|
||||
cairo_destroy ( tb->main_draw );
|
||||
tb->main_draw = NULL;
|
||||
}
|
||||
if ( tb->main_surface ) {
|
||||
cairo_surface_destroy ( tb->main_surface );
|
||||
tb->main_surface = NULL;
|
||||
}
|
||||
|
||||
XDestroyWindow ( display, tb->window );
|
||||
g_free ( tb );
|
||||
}
|
||||
|
||||
void textbox_draw ( textbox *tb )
|
||||
static void texbox_update ( textbox *tb )
|
||||
{
|
||||
GC context = XCreateGC ( display, tb->window, 0, 0 );
|
||||
Pixmap canvas = XCreatePixmap ( display, tb->window, tb->w, tb->h, visual_info->depth );
|
||||
XftDraw *draw = XftDrawCreate ( display, canvas, visual_info->visual, target_colormap );
|
||||
|
||||
// clear canvas
|
||||
XftDrawRect ( draw, &tb->color_bg, 0, 0, tb->w, tb->h );
|
||||
if ( tb->update ) {
|
||||
if ( tb->main_surface ) {
|
||||
cairo_destroy ( tb->main_draw );
|
||||
cairo_surface_destroy ( tb->main_surface );
|
||||
tb->main_draw = NULL;
|
||||
tb->main_surface = NULL;
|
||||
}
|
||||
tb->main_surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, tb->w, tb->h );
|
||||
tb->main_draw = cairo_create ( tb->main_surface );
|
||||
PangoFontDescription *pfd = pango_font_description_from_string ( config.menu_font );
|
||||
pango_font_description_free ( pfd );
|
||||
cairo_set_operator ( tb->main_draw, CAIRO_OPERATOR_SOURCE );
|
||||
|
||||
pango_cairo_update_layout ( tb->main_draw, tb->layout );
|
||||
char *text = tb->text ? tb->text : "";
|
||||
int text_len = strlen ( text );
|
||||
int font_height = textbox_get_font_height ( tb );
|
||||
|
@ -295,35 +259,51 @@ void textbox_draw ( textbox *tb )
|
|||
}
|
||||
|
||||
// Skip the side MARGIN on the X axis.
|
||||
int x = PANGO_SCALE * SIDE_MARGIN;
|
||||
int x = SIDE_MARGIN;
|
||||
int y = 0;
|
||||
|
||||
if ( tb->flags & TB_RIGHT ) {
|
||||
int line_width = 0;
|
||||
// Get actual width.
|
||||
pango_layout_get_pixel_size ( tb->layout, &line_width, NULL );
|
||||
x = ( tb->w - line_width - SIDE_MARGIN ) * PANGO_SCALE;
|
||||
x = ( tb->w - line_width - SIDE_MARGIN );
|
||||
}
|
||||
else if ( tb->flags & TB_CENTER ) {
|
||||
int tw = textbox_get_font_width ( tb );
|
||||
x = ( PANGO_SCALE * ( tb->w - tw - 2 * SIDE_MARGIN ) ) / 2;
|
||||
x = ( ( tb->w - tw - 2 * SIDE_MARGIN ) ) / 2;
|
||||
}
|
||||
y = ( PANGO_SCALE * ( tb->h - textbox_get_font_height ( tb ) ) ) / 2;
|
||||
// Render the layout.
|
||||
pango_xft_render_layout ( draw, &( tb->color_fg ), tb->layout, x, y );
|
||||
y = ( ( tb->h - textbox_get_font_height ( tb ) ) ) / 2;
|
||||
|
||||
// Set ARGB
|
||||
Color col = tb->color_bg;
|
||||
cairo_set_source_rgba ( tb->main_draw, col.red, col.green, col.blue, col.alpha );
|
||||
cairo_paint ( tb->main_draw );
|
||||
|
||||
// Set ARGB
|
||||
col = tb->color_fg;
|
||||
cairo_set_source_rgba ( tb->main_draw, col.red, col.green, col.blue, col.alpha );
|
||||
cairo_move_to ( tb->main_draw, x, y );
|
||||
pango_cairo_show_layout ( tb->main_draw, tb->layout );
|
||||
|
||||
//cairo_fill(tb->draw);
|
||||
// draw the cursor
|
||||
if ( tb->flags & TB_EDITABLE ) {
|
||||
XftDrawRect ( draw, &tb->color_fg, x / PANGO_SCALE + cursor_x, y / PANGO_SCALE, cursor_width, font_height );
|
||||
cairo_rectangle ( tb->main_draw, x + cursor_x, y, cursor_width, font_height );
|
||||
cairo_fill ( tb->main_draw );
|
||||
}
|
||||
|
||||
// flip canvas to window
|
||||
// XClearWindow ( display, tb->window);
|
||||
XCopyArea ( display, canvas, tb->window, context, 0, 0, tb->w, tb->h, 0, 0 );
|
||||
tb->update = FALSE;
|
||||
}
|
||||
}
|
||||
void textbox_draw ( textbox *tb, cairo_t *draw )
|
||||
{
|
||||
texbox_update ( tb );
|
||||
|
||||
XFreeGC ( display, context );
|
||||
XftDrawDestroy ( draw );
|
||||
XFreePixmap ( display, canvas );
|
||||
/* Write buffer */
|
||||
|
||||
cairo_set_source_surface ( draw, tb->main_surface, tb->x, tb->y );
|
||||
cairo_rectangle ( draw, tb->x, tb->y, tb->w, tb->h );
|
||||
cairo_fill ( draw );
|
||||
}
|
||||
|
||||
// cursor handling for edit mode
|
||||
|
@ -331,6 +311,7 @@ void textbox_cursor ( textbox *tb, int pos )
|
|||
{
|
||||
int length = ( tb->text == NULL ) ? 0 : strlen ( tb->text );
|
||||
tb->cursor = MAX ( 0, MIN ( length, pos ) );
|
||||
tb->update = TRUE;
|
||||
}
|
||||
|
||||
// move right
|
||||
|
@ -413,6 +394,7 @@ static void textbox_cursor_dec_word ( textbox *tb )
|
|||
void textbox_cursor_end ( textbox *tb )
|
||||
{
|
||||
tb->cursor = ( int ) strlen ( tb->text );
|
||||
tb->update = TRUE;
|
||||
}
|
||||
|
||||
// insert text
|
||||
|
@ -430,6 +412,7 @@ void textbox_insert ( textbox *tb, int pos, char *str )
|
|||
|
||||
// Set modified, lay out need te be redrawn
|
||||
tb->changed = TRUE;
|
||||
tb->update = TRUE;
|
||||
}
|
||||
|
||||
// remove text
|
||||
|
@ -449,6 +432,7 @@ void textbox_delete ( textbox *tb, int pos, int dlen )
|
|||
}
|
||||
// Set modified, lay out need te be redrawn
|
||||
tb->changed = TRUE;
|
||||
tb->update = TRUE;
|
||||
}
|
||||
|
||||
// delete on character
|
||||
|
@ -495,24 +479,18 @@ static void textbox_cursor_del_word ( textbox *tb )
|
|||
// 0 = unhandled
|
||||
// 1 = handled
|
||||
// -1 = handled and return pressed (finished)
|
||||
int textbox_keypress ( textbox *tb, XEvent *ev )
|
||||
int textbox_keypress ( textbox *tb, XIC xic, XEvent *ev )
|
||||
{
|
||||
KeySym key;
|
||||
Status stat;
|
||||
char pad[32];
|
||||
int len;
|
||||
|
||||
// This is needed for letting the Input Method handle combined keys.
|
||||
// E.g. `e into è
|
||||
if ( XFilterEvent ( ev, tb->window ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( !( tb->flags & TB_EDITABLE ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = Xutf8LookupString ( tb->xic, &ev->xkey, pad, sizeof ( pad ), &key, &stat );
|
||||
len = Xutf8LookupString ( xic, &ev->xkey, pad, sizeof ( pad ), &key, &stat );
|
||||
pad[len] = 0;
|
||||
// Left or Ctrl-b
|
||||
if ( abe_test_action ( MOVE_CHAR_BACK, ev->xkey.state, key ) ) {
|
||||
|
@ -590,41 +568,27 @@ int textbox_keypress ( textbox *tb, XEvent *ev )
|
|||
/***
|
||||
* Font setup.
|
||||
*/
|
||||
static void parse_color ( Visual *visual, Colormap colormap,
|
||||
const char *bg, XftColor *color, const char *def )
|
||||
static void parse_color ( char *bg, Color *col )
|
||||
{
|
||||
if ( bg == NULL ) {
|
||||
return;
|
||||
}
|
||||
if ( strncmp ( bg, "argb:", 5 ) == 0 ) {
|
||||
XRenderColor col;
|
||||
unsigned int val = strtoul ( &bg[5], NULL, 16 );
|
||||
col.alpha = ( ( val & 0xFF000000 ) >> 24 ) * 255;
|
||||
col.red = ( ( val & 0x00FF0000 ) >> 16 ) * 255;
|
||||
col.green = ( ( val & 0x0000FF00 ) >> 8 ) * 255;
|
||||
col.blue = ( ( val & 0x000000FF ) ) * 255;
|
||||
if ( !XftColorAllocValue ( display, visual, colormap, &col, color ) ) {
|
||||
fprintf ( stderr, "Failed to parse color: '%s'\n", bg );
|
||||
// Go for default.
|
||||
if ( !XftColorAllocName ( display, visual, colormap, def, color ) ) {
|
||||
fprintf ( stderr, "Cannot allocate default color, giving up.\n" );
|
||||
exit ( EXIT_FAILURE );
|
||||
}
|
||||
}
|
||||
col->alpha = ( ( val & 0xFF000000 ) >> 24 ) / 256.0;
|
||||
col->red = ( ( val & 0x00FF0000 ) >> 16 ) / 256.0;
|
||||
col->green = ( ( val & 0x0000FF00 ) >> 8 ) / 256.0;
|
||||
col->blue = ( ( val & 0x000000FF ) ) / 256.0;
|
||||
}
|
||||
else {
|
||||
if ( !XftColorAllocName ( display, visual, colormap, bg, color ) ) {
|
||||
fprintf ( stderr, "Failed to parse color: '%s'\n", bg );
|
||||
// Go for default.
|
||||
if ( !XftColorAllocName ( display, visual, colormap, def, color ) ) {
|
||||
fprintf ( stderr, "Cannot allocate default color, giving up.\n" );
|
||||
exit ( EXIT_FAILURE );
|
||||
unsigned int val = strtoul ( &bg[1], NULL, 16 );
|
||||
col->alpha = 1;
|
||||
col->red = ( ( val & 0x00FF0000 ) >> 16 ) / 256.0;
|
||||
col->green = ( ( val & 0x0000FF00 ) >> 8 ) / 256.0;
|
||||
col->blue = ( ( val & 0x000000FF ) ) / 256.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static void textbox_parse_string ( XVisualInfo *visual, Colormap colormap, const char *str,
|
||||
RowColor *color )
|
||||
static void textbox_parse_string ( const char *str, RowColor *color )
|
||||
{
|
||||
if ( str == NULL ) {
|
||||
return;
|
||||
|
@ -637,79 +601,61 @@ static void textbox_parse_string ( XVisualInfo *visual, Colormap colormap, const
|
|||
switch ( index )
|
||||
{
|
||||
case 0:
|
||||
parse_color ( visual->visual, colormap, g_strstrip ( token ), &( color->bg ), "black" );
|
||||
parse_color ( g_strstrip ( token ), &( color->bg ) );
|
||||
break;
|
||||
case 1:
|
||||
parse_color ( visual->visual, colormap, g_strstrip ( token ), &( color->fg ), "white" );
|
||||
parse_color ( g_strstrip ( token ), &( color->fg ) );
|
||||
break;
|
||||
case 2:
|
||||
parse_color ( visual->visual, colormap, g_strstrip ( token ), &( color->bgalt ), "black" );
|
||||
parse_color ( g_strstrip ( token ), &( color->bgalt ) );
|
||||
break;
|
||||
case 3:
|
||||
parse_color ( visual->visual, colormap, g_strstrip ( token ), &( color->hlbg ), "black" );
|
||||
parse_color ( g_strstrip ( token ), &( color->hlbg ) );
|
||||
break;
|
||||
case 4:
|
||||
parse_color ( visual->visual, colormap, g_strstrip ( token ), &( color->hlfg ), "white" );
|
||||
parse_color ( g_strstrip ( token ), &( color->hlfg ) );
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
g_free ( cstr );
|
||||
}
|
||||
void textbox_setup ( XVisualInfo *visual, Colormap colormap )
|
||||
void textbox_setup ( void )
|
||||
{
|
||||
visual_info = visual;
|
||||
target_colormap = colormap;
|
||||
|
||||
if ( config.color_enabled ) {
|
||||
textbox_parse_string ( visual, target_colormap, config.color_normal, &( colors[NORMAL] ) );
|
||||
textbox_parse_string ( visual, target_colormap, config.color_urgent, &( colors[URGENT] ) );
|
||||
textbox_parse_string ( visual, target_colormap, config.color_active, &( colors[ACTIVE] ) );
|
||||
textbox_parse_string ( config.color_normal, &( colors[NORMAL] ) );
|
||||
textbox_parse_string ( config.color_urgent, &( colors[URGENT] ) );
|
||||
textbox_parse_string ( config.color_active, &( colors[ACTIVE] ) );
|
||||
}
|
||||
else {
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_bg, &( colors[NORMAL].bg ), "black" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_fg, &( colors[NORMAL].fg ), "white" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_bg_alt, &( colors[NORMAL].bgalt ), "black" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_hlfg, &( colors[NORMAL].hlfg ), "white" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_hlbg, &( colors[NORMAL].hlbg ), "black" );
|
||||
parse_color ( config.menu_bg, &( colors[NORMAL].bg ) );
|
||||
parse_color ( config.menu_fg, &( colors[NORMAL].fg ) );
|
||||
parse_color ( config.menu_bg_alt, &( colors[NORMAL].bgalt ) );
|
||||
parse_color ( config.menu_hlfg, &( colors[NORMAL].hlfg ) );
|
||||
parse_color ( config.menu_hlbg, &( colors[NORMAL].hlbg ) );
|
||||
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_bg_urgent, &( colors[URGENT].bg ), "black" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_fg_urgent, &( colors[URGENT].fg ), "white" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_bg_alt, &( colors[URGENT].bgalt ), "black" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_hlfg_urgent, &( colors[URGENT].hlfg ), "white" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_hlbg_urgent, &( colors[URGENT].hlbg ), "black" );
|
||||
parse_color ( config.menu_bg_urgent, &( colors[URGENT].bg ) );
|
||||
parse_color ( config.menu_fg_urgent, &( colors[URGENT].fg ) );
|
||||
parse_color ( config.menu_bg_alt, &( colors[URGENT].bgalt ) );
|
||||
parse_color ( config.menu_hlfg_urgent, &( colors[URGENT].hlfg ) );
|
||||
parse_color ( config.menu_hlbg_urgent, &( colors[URGENT].hlbg ) );
|
||||
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_bg_active, &( colors[ACTIVE].bg ), "black" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_fg_active, &( colors[ACTIVE].fg ), "white" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_bg_alt, &( colors[ACTIVE].bgalt ), "black" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_hlfg_active, &( colors[ACTIVE].hlfg ), "white" );
|
||||
parse_color ( visual_info->visual, target_colormap, config.menu_hlbg_active, &( colors[ACTIVE].hlbg ), "black" );
|
||||
parse_color ( config.menu_bg_active, &( colors[ACTIVE].bg ) );
|
||||
parse_color ( config.menu_fg_active, &( colors[ACTIVE].fg ) );
|
||||
parse_color ( config.menu_bg_alt, &( colors[ACTIVE].bgalt ) );
|
||||
parse_color ( config.menu_hlfg_active, &( colors[ACTIVE].hlfg ) );
|
||||
parse_color ( config.menu_hlbg_active, &( colors[ACTIVE].hlbg ) );
|
||||
}
|
||||
PangoFontMap *font_map = pango_xft_get_font_map ( display, DefaultScreen ( display ) );
|
||||
PangoFontMap *font_map = pango_cairo_font_map_new ();
|
||||
p_context = pango_font_map_create_context ( font_map );
|
||||
}
|
||||
|
||||
static void textbox_clean_rowcolor ( RowColor * color )
|
||||
{
|
||||
XftColorFree ( display, visual_info->visual, target_colormap, &( color->fg ) );
|
||||
XftColorFree ( display, visual_info->visual, target_colormap, &( color->bg ) );
|
||||
XftColorFree ( display, visual_info->visual, target_colormap, &( color->bgalt ) );
|
||||
XftColorFree ( display, visual_info->visual, target_colormap, &( color->hlfg ) );
|
||||
XftColorFree ( display, visual_info->visual, target_colormap, &( color->hlbg ) );
|
||||
g_object_unref ( font_map );
|
||||
}
|
||||
|
||||
void textbox_cleanup ( void )
|
||||
{
|
||||
if ( p_context ) {
|
||||
for ( unsigned int st = 0; st < num_states; st++ ) {
|
||||
textbox_clean_rowcolor ( &colors[st] );
|
||||
}
|
||||
|
||||
g_object_unref ( p_context );
|
||||
pango_xft_shutdown_display ( display, DefaultScreen ( display ) );
|
||||
p_context = NULL;
|
||||
visual_info = NULL;
|
||||
target_colormap = None;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -83,11 +83,16 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv )
|
|||
0, 0, 200, 100, config.menu_bw, vinfo.depth, InputOutput,
|
||||
vinfo.visual, CWColormap | CWBorderPixel | CWBackPixel, &attr );
|
||||
TASSERT ( mw != None );
|
||||
|
||||
cairo_surface_t *surface = cairo_xlib_surface_create ( display, mw, vinfo.visual, 200, 100 );
|
||||
// Create a drawable.
|
||||
cairo_t *draw = cairo_create ( surface );
|
||||
cairo_set_operator ( draw, CAIRO_OPERATOR_SOURCE );
|
||||
// Set alternate row to normal row.
|
||||
config.menu_bg_alt = config.menu_bg;
|
||||
textbox_setup ( &vinfo, map );
|
||||
textbox_setup ( );
|
||||
textbox *box =
|
||||
textbox_create ( mw, &vinfo, map, TB_EDITABLE | TB_AUTOWIDTH | TB_AUTOHEIGHT, 0, 0, -1, -1,
|
||||
textbox_create ( TB_EDITABLE | TB_AUTOWIDTH | TB_AUTOHEIGHT, 0, 0, -1, -1,
|
||||
NORMAL,
|
||||
"test" );
|
||||
TASSERT ( box != NULL );
|
||||
|
@ -158,19 +163,19 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv )
|
|||
TASSERT ( box->cursor == 5 );
|
||||
|
||||
textbox_font ( box, HIGHLIGHT );
|
||||
textbox_draw ( box );
|
||||
textbox_draw ( box, draw );
|
||||
|
||||
textbox_show ( box );
|
||||
textbox_move ( box, 12, 13 );
|
||||
TASSERT ( box->x == 12 );
|
||||
TASSERT ( box->y == 13 );
|
||||
textbox_hide ( box );
|
||||
|
||||
textbox_free ( box );
|
||||
textbox_cleanup ( );
|
||||
|
||||
cleanup_abe ();
|
||||
|
||||
cairo_destroy ( draw );
|
||||
cairo_surface_destroy ( surface );
|
||||
XDestroyWindow ( display, mw );
|
||||
XCloseDisplay ( display );
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue