Update scrollbar behaviour, so dragging is more natural. (limit range from hw/2 to h-hw/2)

This commit is contained in:
Dave Davenport 2016-10-25 21:19:39 +02:00
parent 8a186726f8
commit a38ac9caf3
14 changed files with 160 additions and 89 deletions

View file

@ -46,16 +46,16 @@ typedef struct
*/
typedef enum
{
TB_AUTOHEIGHT = 1 << 0,
TB_AUTOWIDTH = 1 << 1,
TB_LEFT = 1 << 16,
TB_RIGHT = 1 << 17,
TB_CENTER = 1 << 18,
TB_EDITABLE = 1 << 19,
TB_MARKUP = 1 << 20,
TB_WRAP = 1 << 21,
TB_PASSWORD = 1 << 22,
TB_INDICATOR = 1 << 23,
TB_AUTOHEIGHT = 1 << 0,
TB_AUTOWIDTH = 1 << 1,
TB_LEFT = 1 << 16,
TB_RIGHT = 1 << 17,
TB_CENTER = 1 << 18,
TB_EDITABLE = 1 << 19,
TB_MARKUP = 1 << 20,
TB_WRAP = 1 << 21,
TB_PASSWORD = 1 << 22,
TB_INDICATOR = 1 << 23,
} TextboxFlags;
/**
* Flags indicating current state of the textbox.

View file

@ -35,6 +35,8 @@ struct _widget
/** update widget implementation function */
void ( *update )( struct _widget * );
gboolean (*motion_notify)( struct _widget *, xcb_motion_notify_event_t * );
/** widget clicked callback */
widget_clicked_cb clicked;
/** user data for ::clicked callback */

View file

@ -164,5 +164,14 @@ gboolean widget_clicked ( widget *wid, xcb_button_press_event_t *xbe );
*/
void widget_set_clicked_handler ( widget *wid, widget_clicked_cb cb, void *udata );
/**
* @param wid The widget handle
* @param xmd The motion notify object.
*
* Motion notify.
* @TODO make this like clicked with callback.
* returns TRUE when handled.
*/
gboolean widget_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme );
/*@}*/
#endif // ROFI_WIDGET_H

View file

@ -198,7 +198,7 @@ static char **read_hosts_file ( char ** retv, unsigned int *length )
// Reading one line per time.
while ( getline ( &buffer, &buffer_length, fd ) > 0 ) {
// Evaluate one line.
unsigned int index = 0, ti = 0;
unsigned int index = 0, ti = 0;
char *token = buffer;
// Tokenize it.

View file

@ -194,7 +194,7 @@ static GRegex * create_regex ( const char *input, int case_sensitive )
{
#define R( s ) g_regex_new ( s, G_REGEX_OPTIMIZE | ( ( case_sensitive ) ? 0 : G_REGEX_CASELESS ), 0, NULL )
GRegex * retv = NULL;
gchar *r;
gchar *r;
switch ( config.matching_method )
{
case MM_GLOB:
@ -234,7 +234,7 @@ GRegex **tokenize ( const char *input, int case_sensitive )
}
char *saveptr = NULL, *token;
GRegex **retv = NULL;
GRegex **retv = NULL;
if ( !config.tokenize ) {
retv = g_malloc0 ( sizeof ( GRegex* ) * 2 );
retv[0] = (GRegex *) create_regex ( input, case_sensitive );

View file

@ -344,7 +344,7 @@ static int add_mode ( const char * token )
}
else
#endif // WINDOW_MODE
// SSh dialog
// SSh dialog
if ( strcasecmp ( token, "ssh" ) == 0 ) {
modi[num_modi] = &ssh_mode;
num_modi++;

View file

@ -698,7 +698,7 @@ static void update_callback ( textbox *t, unsigned int index, void *udata, TextB
// Move into list view.
textbox_text ( t, text );
if ( state->tokens && config.show_match ) {
if ( state->tokens && config.show_match ) {
PangoAttrList *list = textbox_get_pango_attributes ( t );
if ( list != NULL ) {
pango_attr_list_ref ( list );
@ -1229,6 +1229,12 @@ static void rofi_view_mainloop_iter ( RofiViewState *state, xcb_generic_event_t
// xme->event_x >= state->scrollbar->widget.x && xme->event_x < ( state->scrollbar->widget.x + state->scrollbar->widget.w ) ) {
// state->selected = scrollbar_clicked ( state->scrollbar, xme->event_y );
// }
xcb_motion_notify_event_t xme = *( (xcb_motion_notify_event_t *) ev );
xme.event_x -= config.padding;
xme.event_y -= config.padding;
if ( widget_motion_notify ( WIDGET ( state->main_box ), &xme ) ) {
return;
}
break;
}
case XCB_BUTTON_PRESS:

View file

@ -250,21 +250,39 @@ static gboolean box_clicked ( widget *wid, xcb_button_press_event_t *xbe, G_GNUC
}
return FALSE;
}
static gboolean box_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme )
{
box *b = (box *) wid;
for ( GList *iter = g_list_first ( b->children ); iter != NULL; iter = g_list_next ( iter ) ) {
widget * child = (widget *) iter->data;
if ( !child->enabled ) {
continue;
}
if ( widget_intersect ( child, xme->event_x, xme->event_y ) ) {
xcb_motion_notify_event_t rel = *xme;
rel.event_x -= child->x;
rel.event_y -= child->y;
return widget_motion_notify ( child, &rel );
}
}
return FALSE;
}
box * box_create ( boxType type, short x, short y, short w, short h )
{
box *b = g_malloc0 ( sizeof ( box ) );
b->type = type;
b->widget.x = x;
b->widget.y = y;
b->widget.w = w;
b->widget.h = h;
b->widget.draw = box_draw;
b->widget.free = box_free;
b->widget.resize = box_resize;
b->widget.update = box_update;
b->widget.clicked = box_clicked;
b->widget.enabled = TRUE;
b->type = type;
b->widget.x = x;
b->widget.y = y;
b->widget.w = w;
b->widget.h = h;
b->widget.draw = box_draw;
b->widget.free = box_free;
b->widget.resize = box_resize;
b->widget.update = box_update;
b->widget.clicked = box_clicked;
b->widget.motion_notify = box_motion_notify;
b->widget.enabled = TRUE;
return b;
}

View file

@ -66,6 +66,8 @@ struct _listview
listview_update_callback callback;
void *udata;
gboolean scrollbar_scroll;
xcb_timestamp_t last_click;
listview_mouse_activated_cb mouse_activated;
void *mouse_activated_data;
@ -100,7 +102,7 @@ static unsigned int scroll_per_page ( listview * lv )
lv->rchanged = TRUE;
}
// Set the position
scrollbar_set_handle ( lv->scrollbar, page * lv->max_elements );
//scrollbar_set_handle ( lv->scrollbar, page * lv->max_elements );
}
return offset;
}
@ -119,7 +121,7 @@ static unsigned int scroll_continious ( listview *lv )
}
}
if ( offset != lv->cur_page ) {
scrollbar_set_handle ( lv->scrollbar, offset );
//scrollbar_set_handle ( lv->scrollbar, offset );
lv->cur_page = offset;
lv->rchanged = TRUE;
}
@ -147,6 +149,7 @@ static void listview_draw ( widget *wid, cairo_t *draw )
else {
offset = scroll_per_page ( lv );
}
scrollbar_set_handle ( lv->scrollbar, lv->selected );
lv->last_offset = offset;
if ( lv->cur_elements > 0 && lv->max_rows > 0 ) {
cairo_save ( draw );
@ -263,11 +266,13 @@ static gboolean listview_scrollbar_clicked ( widget *sb, xcb_button_press_event_
static gboolean listview_clicked ( widget *wid, xcb_button_press_event_t *xce, G_GNUC_UNUSED void *udata )
{
listview *lv = (listview *) wid;
lv->scrollbar_scroll = FALSE;
if ( widget_enabled ( WIDGET ( lv->scrollbar ) ) && widget_intersect ( WIDGET ( lv->scrollbar ), xce->event_x, xce->event_y ) ) {
// Forward to handler of scrollbar.
xcb_button_press_event_t xce2 = *xce;
xce->event_x -= widget_get_x_pos ( WIDGET ( lv->scrollbar ) );
xce->event_y -= widget_get_y_pos ( WIDGET ( lv->scrollbar ) );
xce->event_x -= widget_get_x_pos ( WIDGET ( lv->scrollbar ) );
xce->event_y -= widget_get_y_pos ( WIDGET ( lv->scrollbar ) );
lv->scrollbar_scroll = TRUE;
return widget_clicked ( WIDGET ( lv->scrollbar ), &xce2 );
}
// Handle the boxes.
@ -291,14 +296,28 @@ static gboolean listview_clicked ( widget *wid, xcb_button_press_event_t *xce, G
return FALSE;
}
static gboolean listview_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme )
{
listview *lv = (listview *) wid;
if ( widget_enabled ( WIDGET ( lv->scrollbar ) ) && lv->scrollbar_scroll ) {
xcb_motion_notify_event_t xle = *xme;
xle.event_x -= wid->x;
xle.event_y -= wid->y;
widget_motion_notify ( WIDGET ( lv->scrollbar ), &xle );
return TRUE;
}
return FALSE;
}
listview *listview_create ( listview_update_callback cb, void *udata, unsigned int eh )
{
listview *lv = g_malloc0 ( sizeof ( listview ) );
lv->widget.free = listview_free;
lv->widget.resize = listview_resize;
lv->widget.draw = listview_draw;
lv->widget.clicked = listview_clicked;
lv->widget.enabled = TRUE;
lv->widget.free = listview_free;
lv->widget.resize = listview_resize;
lv->widget.draw = listview_draw;
lv->widget.clicked = listview_clicked;
lv->widget.motion_notify = listview_motion_notify;
lv->widget.enabled = TRUE;
lv->scrollbar = scrollbar_create ( 0, 0, 4, 0 );
widget_set_clicked_handler ( WIDGET ( lv->scrollbar ), listview_scrollbar_clicked, lv );

View file

@ -31,6 +31,7 @@
static void scrollbar_draw ( widget *, cairo_t * );
static void scrollbar_free ( widget * );
static gboolean scrollbar_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme );
scrollbar *scrollbar_create ( short x, short y, short w, short h )
{
@ -41,8 +42,9 @@ scrollbar *scrollbar_create ( short x, short y, short w, short h )
sb->widget.w = MAX ( 1, w );
sb->widget.h = MAX ( 1, h );
sb->widget.draw = scrollbar_draw;
sb->widget.free = scrollbar_free;
sb->widget.draw = scrollbar_draw;
sb->widget.free = scrollbar_free;
sb->widget.motion_notify = scrollbar_motion_notify;
sb->length = 10;
sb->pos = 0;
@ -84,22 +86,27 @@ static void scrollbar_draw ( widget *wid, cairo_t *draw )
{
scrollbar *sb = (scrollbar *) wid;
// Calculate position and size.
const short bh = sb->widget.h - 0;
float sec = ( ( bh ) / (float) sb->length );
const short bh = sb->widget.h;
float sec = ( ( bh ) / (float) ( sb->length + sb->pos_length - 2 ) );
short height = sb->pos_length * sec;
short y = sb->pos * sec;
// Set max pos.
y = MIN ( y, bh - 2 );
y = MIN ( y, bh );
// Never go out of bar.
height = MAX ( 2, height );
// Cap length;
height = MIN ( bh - y + 1, ( height ) );
// Redraw base window
color_separator ( draw );
cairo_rectangle ( draw, sb->widget.x, sb->widget.y + y, sb->widget.w, height );
cairo_fill ( draw );
}
static gboolean scrollbar_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme )
{
xcb_button_press_event_t xle;
xle.event_x = xme->event_x;
xle.event_y = xme->event_y;
return widget_clicked ( WIDGET ( wid ), &xle );
}
// TODO
// This should behave more like a real scrollbar.
@ -107,10 +114,12 @@ unsigned int scrollbar_clicked ( const scrollbar *sb, int y )
{
if ( sb != NULL ) {
if ( y >= sb->widget.y && y <= ( sb->widget.y + sb->widget.h ) ) {
y -= sb->widget.y;
y = MIN ( MAX ( 0, y ), sb->widget.h );
const short bh = sb->widget.h;
float sec = ( ( bh ) / (float) sb->length );
const short bh = sb->widget.h;
float sec = ( ( bh ) / (float) ( sb->length + sb->pos_length ) );
unsigned int half_handle = MAX ( 1, sec * ( sb->pos_length / 2.0 ) );
y -= sb->widget.y + half_handle;
y = MIN ( MAX ( 0, y ), sb->widget.h - 2 * half_handle );
unsigned int sel = y / sec;
return MIN ( sel, sb->length - 1 );
}

View file

@ -154,3 +154,12 @@ void widget_set_clicked_handler ( widget *wid, widget_clicked_cb cb, void *udata
wid->clicked_cb_data = udata;
}
}
gboolean widget_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme )
{
if ( wid && wid->motion_notify ) {
wid->motion_notify ( wid, xme );
}
return FALSE;
}

View file

@ -82,36 +82,36 @@ int main ( int argc, char ** argv )
* Quick converision check.
*/
{
char *str = rofi_latin_to_utf8_strdup ( "\xA1\xB5", 2);
TASSERT ( g_utf8_collate ( str, "¡µ") == 0 );
g_free(str);
char *str = rofi_latin_to_utf8_strdup ( "\xA1\xB5", 2 );
TASSERT ( g_utf8_collate ( str, "¡µ" ) == 0 );
g_free ( str );
}
{
char *str = rofi_force_utf8("Valid utf8", 10);
TASSERT ( g_utf8_collate ( str, "Valid utf8") == 0 );
g_free(str);
char *str = rofi_force_utf8 ( "Valid utf8", 10 );
TASSERT ( g_utf8_collate ( str, "Valid utf8" ) == 0 );
g_free ( str );
char in[] = "Valid utf8 until \xc3\x28 we continue here";
TASSERT ( g_utf8_validate ( in, -1, NULL ) == FALSE );
str = rofi_force_utf8(in, strlen(in));
str = rofi_force_utf8 ( in, strlen ( in ) );
TASSERT ( g_utf8_validate ( str, -1, NULL ) == TRUE );
TASSERT ( g_utf8_collate ( str, "Valid utf8 until <20>( we continue here") == 0 );
g_free(str);
}
TASSERT ( g_utf8_collate ( str, "Valid utf8 until <20>( we continue here" ) == 0 );
g_free ( str );
}
// Pid test.
// Tests basic functionality of writing it, locking, seeing if I can write same again
// And close/reopen it again.
{
const char *path = "/tmp/rofi-test.pid";
TASSERT( create_pid_file ( NULL ) == -1 );
int fd = create_pid_file ( path );
TASSERT( fd >= 0 );
int fd2 = create_pid_file ( path );
TASSERT ( fd2 < 0 );
const char *path = "/tmp/rofi-test.pid";
TASSERT ( create_pid_file ( NULL ) == -1 );
int fd = create_pid_file ( path );
TASSERT ( fd >= 0 );
int fd2 = create_pid_file ( path );
TASSERT ( fd2 < 0 );
remove_pid_file ( fd );
fd = create_pid_file ( path );
TASSERT( fd >= 0 );
TASSERT ( fd >= 0 );
remove_pid_file ( fd );
}
}

View file

@ -52,24 +52,24 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv )
TASSERTE ( sb->pos_length, 1 );
unsigned int cl = scrollbar_clicked ( sb, 10 );
TASSERTE ( cl, 1000);
TASSERTE ( cl, 900);
cl = scrollbar_clicked ( sb, 20 );
TASSERTE ( cl, 2000);
TASSERTE ( cl, 1900);
cl = scrollbar_clicked ( sb, 0 );
TASSERTE ( cl, 0);
cl = scrollbar_clicked ( sb, 99 );
TASSERTE ( cl, 9900);
TASSERTE ( cl, 9800);
scrollbar_set_max_value ( sb, 100 );
for ( unsigned int i = 0; i < 100; i++ ){
for ( unsigned int i = 1; i < 99; i++ ){
cl = scrollbar_clicked ( sb, i );
TASSERTE ( cl, i);
TASSERTE ( cl, i-1);
}
scrollbar_set_max_value ( sb, 200 );
for ( unsigned int i = 0; i < 100; i++ ){
for ( unsigned int i = 1; i < 100; i++ ){
cl = scrollbar_clicked ( sb, i );
TASSERTE ( cl, i*2);
TASSERTE ( cl, i*2-2);
}
widget_free( WIDGET (sb ) );
}

View file

@ -88,9 +88,9 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv )
TASSERT ( strcmp ( box->text, "tesb" ) == 0 );
textbox_keybinding ( box, MOVE_CHAR_BACK );
TASSERT ( box->cursor == 3 );
textbox_keybinding ( box, MOVE_CHAR_FORWARD);
textbox_keybinding ( box, MOVE_CHAR_FORWARD );
TASSERT ( box->cursor == 4 );
textbox_keybinding ( box, MOVE_CHAR_FORWARD);
textbox_keybinding ( box, MOVE_CHAR_FORWARD );
TASSERT ( box->cursor == 4 );
// Cursor after delete section.
textbox_delete ( box, 0, 1 );
@ -122,32 +122,31 @@ int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv )
TASSERT ( strcmp ( box->text, "aapmies" ) == 0 );
TASSERT ( box->cursor == 5 );
textbox_text ( box, "aap noot mies");
textbox_text ( box, "aap noot mies" );
textbox_cursor ( box, 8 );
textbox_keybinding ( box, REMOVE_WORD_BACK );
TASSERT ( box->cursor == 4);
TASSERT ( strcmp ( box->text, "aap mies") == 0 );
TASSERT ( box->cursor == 4 );
TASSERT ( strcmp ( box->text, "aap mies" ) == 0 );
textbox_keybinding ( box, REMOVE_TO_EOL );
TASSERT ( box->cursor == 4);
TASSERT ( strcmp ( box->text, "aap ") == 0 );
textbox_text ( box, "aap noot mies");
TASSERT ( box->cursor == 4 );
TASSERT ( strcmp ( box->text, "aap " ) == 0 );
textbox_text ( box, "aap noot mies" );
textbox_cursor ( box, 8 );
textbox_keybinding ( box, REMOVE_WORD_FORWARD );
TASSERT ( strcmp ( box->text, "aap noot") == 0 );
TASSERT ( strcmp ( box->text, "aap noot" ) == 0 );
textbox_keybinding ( box, MOVE_FRONT );
TASSERT ( box->cursor == 0);
TASSERT ( box->cursor == 0 );
textbox_keybinding ( box, CLEAR_LINE );
TASSERT ( strcmp ( box->text, "") == 0 );
textbox_text ( box, "aap noot mies");
textbox_keybinding ( box, MOVE_END);
TASSERT ( strcmp ( box->text, "" ) == 0 );
textbox_text ( box, "aap noot mies" );
textbox_keybinding ( box, MOVE_END );
textbox_keybinding ( box, MOVE_WORD_BACK );
TASSERT ( box->cursor == 9);
TASSERT ( box->cursor == 9 );
textbox_keybinding ( box, MOVE_WORD_BACK );
TASSERT ( box->cursor == 4);
TASSERT ( box->cursor == 4 );
textbox_keybinding ( box, REMOVE_TO_SOL );
TASSERT ( strcmp ( box->text, "noot mies") == 0 );
TASSERT ( box->cursor == 0);
TASSERT ( strcmp ( box->text, "noot mies" ) == 0 );
TASSERT ( box->cursor == 0 );
textbox_font ( box, HIGHLIGHT );
//textbox_draw ( box, draw );