mudgangster

Log | Files | Refs

commit 5ea1cd6e3f859618122440362fa06d56dad935ca
parent df3232724f2d62ff014f73d37f00947dd845f736
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Tue,  4 Sep 2018 15:38:03 +0300

Messy text highlighting

Diffstat:
src/common.h | 7+++++++
src/textbox.cc | 52+++++++++++++++++++++++++++++++++++++++++++++++-----
src/textbox.h | 4++++
src/ui.h | 2+-
src/x11.cc | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
5 files changed, 176 insertions(+), 35 deletions(-)

diff --git a/src/common.h b/src/common.h @@ -22,6 +22,13 @@ constexpr T max( T a, T b ) { return a > b ? a : b; } +template< typename T > +void swap( T & a, T & b ) { + T t = a; + a = b; + b = t; +} + template< typename To, typename From > inline To checked_cast( const From & from ) { To result = To( from ); diff --git a/src/textbox.cc b/src/textbox.cc @@ -99,6 +99,37 @@ void textbox_set_size( TextBox * tb, int w, int h ) { tb->h = h; } +static bool inside_selection( int col, int row, int start_col, int start_row, int end_col, int end_row ) { + int min_col = min( start_col, end_col ); + int min_row = min( start_row, end_row ); + int max_col = max( start_col, end_col ); + int max_row = max( start_row, end_row ); + + if( row < min_row || row > max_row ) + return false; + + if( start_row == end_row ) + return col >= min_col && col <= max_col; + + if( row > min_row && row < max_row ) + return true; + + if( start_row < end_row ) { + if( row == start_row ) + return col <= start_col; + if( row == end_row ) + return col >= end_col; + } + else { + if( row == start_row ) + return col >= start_col; + if( row == end_row ) + return col <= end_col; + } + + return false; +} + void textbox_draw( const TextBox * tb ) { if( tb->w == 0 || tb->h == 0 ) return; @@ -119,6 +150,9 @@ void textbox_draw( const TextBox * tb ) { size_t tb_rows = num_rows( tb->h ); size_t tb_cols = tb->w / fw; + int top_spacing = SPACING / 2; + int bot_spacing = SPACING - top_spacing; + while( rows_drawn < tb_rows && lines_drawn + tb->scroll_offset < tb->num_lines ) { const TextBox::Line & line = tb->lines[ ( tb->head + tb->num_lines - tb->scroll_offset - lines_drawn ) % tb->max_lines ]; @@ -130,8 +164,9 @@ void textbox_draw( const TextBox * tb ) { const TextBox::Glyph & glyph = line.glyphs[ i ]; size_t row = i / tb_cols; + size_t col = i % tb_cols; - int left = ( i % tb_cols ) * fw; + int left = col * fw; int top = tb->h - ( rows_drawn + line_rows - row ) * ( fh + SPACING ); if( top < 0 ) continue; @@ -139,14 +174,21 @@ void textbox_draw( const TextBox * tb ) { int fg, bg, bold; unpack_style( glyph.style, &fg, &bg, &bold ); + bool bold_fg = bold; + bool bold_bg = false; + if( tb->selecting ) { + if( inside_selection( col, rows_drawn + line_rows - row - 1, tb->selection_start_col, tb->selection_start_row, tb->selection_end_col, tb->selection_end_row ) ) { + swap( fg, bg ); + swap( bold_fg, bold_bg ); + } + } + // bg // TODO: top/bottom spacing seems to be inconsistent here, try with large spacing - int top_spacing = SPACING / 2; - int bot_spacing = SPACING - top_spacing; - ui_fill_rect( tb->x + left, tb->y + top - top_spacing, fw, fh + bot_spacing, Colour( bg ), false ); + ui_fill_rect( tb->x + left, tb->y + top - top_spacing, fw, fh + bot_spacing, Colour( bg ), bold_bg ); // fg - ui_draw_char( tb->x + left, tb->y + top, glyph.ch, Colour( fg ), bold ); + ui_draw_char( tb->x + left, tb->y + top, glyph.ch, Colour( fg ), bold_fg, bold ); } lines_drawn++; diff --git a/src/textbox.h b/src/textbox.h @@ -24,6 +24,10 @@ struct TextBox { int x, y; int w, h; size_t scroll_offset; + + bool selecting; + int selection_start_col, selection_start_row; + int selection_end_col, selection_end_row; }; void textbox_init( TextBox * tb, size_t scrollback ); diff --git a/src/ui.h b/src/ui.h @@ -38,7 +38,7 @@ void ui_chat_newline(); void ui_chat_print( const char * str, size_t len, Colour fg, Colour bg, bool bold ); void ui_fill_rect( int left, int top, int width, int height, Colour colour, bool bold ); -void ui_draw_char( int left, int top, char c, Colour colour, bool bold ); +void ui_draw_char( int left, int top, char c, Colour colour, bool bold, bool bold_font = false ); void ui_dirty( int left, int top, int right, int bottom ); // TODO: x/y + w/h? void ui_get_font_size( int * fw, int * fh ); diff --git a/src/x11.cc b/src/x11.cc @@ -108,7 +108,7 @@ void ui_fill_rect( int left, int top, int width, int height, Colour colour, bool XFillRectangle( UI.display, UI.back_buffer, UI.gc, left, top, width, height ); } -void ui_draw_char( int left, int top, char c, Colour colour, bool bold ) { +void ui_draw_char( int left, int top, char c, Colour colour, bool bold, bool bold_font ) { int left_spacing = Style.font.width / 2; int right_spacing = Style.font.width - left_spacing; int line_height = Style.font.height + SPACING; @@ -262,7 +262,7 @@ void ui_draw_char( int left, int top, char c, Colour colour, bool bold ) { return; } - XSetFont( UI.display, UI.gc, ( bold ? Style.fontBold : Style.font ).font->fid ); + XSetFont( UI.display, UI.gc, ( bold || bold_font ? Style.fontBold : Style.font ).font->fid ); set_fg( colour, bold ); XDrawString( UI.display, UI.back_buffer, UI.gc, left, top + Style.font.ascent + SPACING, &c, 1 ); } @@ -370,22 +370,106 @@ void ui_draw() { ui_dirty( 0, 0, UI.width, UI.height ); } -static void eventButtonPress( XEvent * event ) { } +static void event_mouse_down( XEvent * xevent ) { + const XButtonEvent * event = &xevent->xbutton; -static void eventButtonRelease( XEvent * event ) { } + if( event->x >= UI.main_text.x && event->x < UI.main_text.x + UI.main_text.w && event->y >= UI.main_text.y && event->y < UI.main_text.y + UI.main_text.h ) { + int x = event->x - UI.main_text.x; + int y = event->y - UI.main_text.y; -static void eventMessage( XEvent * event ) { - if( ( Atom ) event->xclient.data.l[ 0 ] == wmDeleteWindow ) { + int my = UI.main_text.h - y; + + int row = my / ( Style.font.height + SPACING ); + int col = x / Style.font.width; + + UI.main_text.selecting = true; + UI.main_text.selection_start_col = col; + UI.main_text.selection_start_row = row; + UI.main_text.selection_end_col = col; + UI.main_text.selection_end_row = row; + + textbox_draw( &UI.main_text ); + } + + if( event->x >= UI.chat_text.x && event->x < UI.chat_text.x + UI.chat_text.w && event->y >= UI.chat_text.y && event->y < UI.chat_text.y + UI.chat_text.h ) { + int x = event->x - UI.chat_text.x; + int y = event->y - UI.chat_text.y; + + int my = UI.chat_text.h - y; + + int row = my / ( Style.font.height + SPACING ); + int col = x / Style.font.width; + + UI.chat_text.selecting = true; + UI.chat_text.selection_start_col = col; + UI.chat_text.selection_start_row = row; + UI.chat_text.selection_end_col = col; + UI.chat_text.selection_end_row = row; + + textbox_draw( &UI.chat_text ); + } +} + +static void event_mouse_up( XEvent * xevent ) { + const XButtonEvent * event = &xevent->xbutton; + + if( UI.main_text.selecting ) { + UI.main_text.selecting = false; + textbox_draw( &UI.main_text ); + } + + if( UI.chat_text.selecting ) { + UI.chat_text.selecting = false; + textbox_draw( &UI.chat_text ); + } +} + +static void event_mouse_move( XEvent * xevent ) { + const XMotionEvent * event = &xevent->xmotion; + + if( UI.main_text.selecting ) { + int x = event->x - UI.main_text.x; + int y = event->y - UI.main_text.y; + + int my = UI.main_text.h - y; + + int row = my / ( Style.font.height + SPACING ); + int col = x / Style.font.width; + + UI.main_text.selection_end_col = col; + UI.main_text.selection_end_row = row; + + textbox_draw( &UI.main_text ); + } + + if( UI.chat_text.selecting ) { + int x = event->x - UI.main_text.x; + int y = event->y - UI.main_text.y; + + int my = UI.main_text.h - y; + + int row = my / ( Style.font.height + SPACING ); + int col = x / Style.font.width; + + UI.main_text.selection_end_col = col; + UI.main_text.selection_end_row = row; + + textbox_draw( &UI.chat_text ); + } +} + +static void event_message( XEvent * xevent ) { + if( ( Atom ) xevent->xclient.data.l[ 0 ] == wmDeleteWindow ) { script_handleClose(); } } -static void eventResize( XEvent * event ) { +static void event_resize( XEvent * xevent ) { int old_width = UI.width; int old_height = UI.height; - UI.width = event->xconfigure.width; - UI.height = event->xconfigure.height; + UI.width = xevent->xconfigure.width; + UI.height = xevent->xconfigure.height; if( UI.width == old_width && UI.height == old_height ) return; @@ -412,26 +496,26 @@ static void eventResize( XEvent * event ) { ); } -static void eventExpose( XEvent * event ) { +static void event_expose( XEvent * xevent ) { ui_draw(); } -static void eventKeyPress( XEvent * event ) { +static void event_key_press( XEvent * xevent ) { #define ADD_MACRO( key, name ) \ case key: \ script_doMacro( name, sizeof( name ) - 1, shift, ctrl, alt ); \ break - XKeyEvent * keyEvent = &event->xkey; + XKeyEvent * event = &xevent->xkey; char keyBuffer[ 32 ]; KeySym key; - bool shift = keyEvent->state & ShiftMask; - bool ctrl = keyEvent->state & ControlMask; - bool alt = keyEvent->state & Mod1Mask; + bool shift = event->state & ShiftMask; + bool ctrl = event->state & ControlMask; + bool alt = event->state & Mod1Mask; - int len = XLookupString( keyEvent, keyBuffer, sizeof( keyBuffer ), &key, NULL ); + int len = XLookupString( event, keyBuffer, sizeof( keyBuffer ), &key, NULL ); switch( key ) { case XK_Return: @@ -549,11 +633,11 @@ static void eventKeyPress( XEvent * event ) { #undef ADD_MACRO } -static void eventFocusOut( XEvent * event ) { +static void event_defocus( XEvent * xevent ) { UI.has_focus = false; } -static void eventFocusIn( XEvent * event ) { +static void event_focus( XEvent * xevent ) { UI.has_focus = true; XWMHints * hints = XGetWMHints( UI.display, UI.window ); @@ -564,18 +648,20 @@ static void eventFocusIn( XEvent * event ) { void ui_handleXEvents() { void ( *event_handlers[ LASTEvent ] )( XEvent * ) = { }; - event_handlers[ ButtonPress ] = eventButtonPress; - event_handlers[ ButtonRelease ] = eventButtonRelease; - event_handlers[ ClientMessage ] = eventMessage; - event_handlers[ ConfigureNotify ] = eventResize; - event_handlers[ Expose ] = eventExpose; - event_handlers[ KeyPress ] = eventKeyPress; - event_handlers[ FocusOut ] = eventFocusOut; - event_handlers[ FocusIn ] = eventFocusIn; + event_handlers[ ButtonPress ] = event_mouse_down; + event_handlers[ ButtonRelease ] = event_mouse_up; + event_handlers[ MotionNotify ] = event_mouse_move; + event_handlers[ ClientMessage ] = event_message; + event_handlers[ ConfigureNotify ] = event_resize; + event_handlers[ Expose ] = event_expose; + event_handlers[ KeyPress ] = event_key_press; + event_handlers[ FocusOut ] = event_defocus; + event_handlers[ FocusIn ] = event_focus; const char * event_names[ LASTEvent ] = { }; event_names[ ButtonPress ] = "ButtonPress"; event_names[ ButtonRelease ] = "ButtonRelease"; + event_names[ MotionNotify ] = "MotionNotify"; event_names[ ClientMessage ] = "ClientMessage"; event_names[ ConfigureNotify ] = "ConfigureNotify"; event_names[ Expose ] = "Expose"; @@ -587,8 +673,10 @@ void ui_handleXEvents() { XEvent event; XNextEvent( UI.display, &event ); - if( event_handlers[ event.type ] != NULL ) + if( event_handlers[ event.type ] != NULL ) { + // printf( "%s\n", event_names[ event.type ] ); event_handlers[ event.type ]( &event ); + } } // if( UI.dirty ) { @@ -669,8 +757,8 @@ void ui_init() { initStyle(); XSetWindowAttributes attr = { }; - attr.event_mask = ExposureMask | StructureNotifyMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask, - attr.colormap = UI.colorMap, + attr.event_mask = ExposureMask | StructureNotifyMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask; + attr.colormap = UI.colorMap; UI.window = XCreateWindow( UI.display, root, 0, 0, 800, 600, 0, UI.depth, InputOutput, visual, CWBackPixel | CWEventMask | CWColormap, &attr ); UI.gc = XCreateGC( UI.display, UI.window, 0, NULL );