GUAC-803: Separate scrollbar graphical update from state update.
This commit is contained in:
parent
1218a18bf4
commit
0f4e45e39d
@ -51,6 +51,18 @@ guac_terminal_scrollbar* guac_terminal_scrollbar_alloc(guac_client* client,
|
|||||||
scrollbar->parent_height = 0;
|
scrollbar->parent_height = 0;
|
||||||
scrollbar->visible_area = 0;
|
scrollbar->visible_area = 0;
|
||||||
|
|
||||||
|
/* Init handle render state */
|
||||||
|
scrollbar->render_state.handle_x = 0;
|
||||||
|
scrollbar->render_state.handle_y = 0;
|
||||||
|
scrollbar->render_state.handle_width = 0;
|
||||||
|
scrollbar->render_state.handle_height = 0;
|
||||||
|
|
||||||
|
/* Init container render state */
|
||||||
|
scrollbar->render_state.container_x = 0;
|
||||||
|
scrollbar->render_state.container_y = 0;
|
||||||
|
scrollbar->render_state.container_width = 0;
|
||||||
|
scrollbar->render_state.container_height = 0;
|
||||||
|
|
||||||
/* Allocate and init layers */
|
/* Allocate and init layers */
|
||||||
scrollbar->container = guac_client_alloc_layer(client);
|
scrollbar->container = guac_client_alloc_layer(client);
|
||||||
scrollbar->handle = guac_client_alloc_layer(client);
|
scrollbar->handle = guac_client_alloc_layer(client);
|
||||||
@ -75,54 +87,156 @@ void guac_terminal_scrollbar_free(guac_terminal_scrollbar* scrollbar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the position and size of the scrollbar handle relative to the
|
* Calculates the render state of the scroll bar, given its minimum, maximum,
|
||||||
* current scrollbar value, bounds, and parent layer dimensions.
|
* and current values. This render state will not be reflected graphically
|
||||||
|
* unless the scrollbar is flushed.
|
||||||
*
|
*
|
||||||
* @param scrollbar
|
* @param scrollbar
|
||||||
* The scrollbar whose handle should be updated.
|
* The scrollbar whose render state should be calculated.
|
||||||
|
*
|
||||||
|
* @param render_state
|
||||||
|
* A pointer to an existing guac_terminal_scrollbar_render_state that will
|
||||||
|
* be populated with the calculated result.
|
||||||
*/
|
*/
|
||||||
static void __update_handle(guac_terminal_scrollbar* scrollbar) {
|
static void calculate_render_state(guac_terminal_scrollbar* scrollbar,
|
||||||
|
guac_terminal_scrollbar_render_state* render_state) {
|
||||||
|
|
||||||
|
/* Calculate container dimensions */
|
||||||
|
render_state->container_width = GUAC_TERMINAL_SCROLLBAR_WIDTH;
|
||||||
|
render_state->container_height = scrollbar->parent_height;
|
||||||
|
|
||||||
|
/* Calculate container position */
|
||||||
|
render_state->container_x = scrollbar->parent_width
|
||||||
|
- render_state->container_width;
|
||||||
|
|
||||||
|
render_state->container_y = 0;
|
||||||
|
|
||||||
|
/* Calculate handle dimensions */
|
||||||
|
render_state->handle_width = render_state->container_width
|
||||||
|
- GUAC_TERMINAL_SCROLLBAR_PADDING*2;
|
||||||
|
|
||||||
|
/* Handle can be no bigger than the scrollbar itself */
|
||||||
|
int max_handle_height = render_state->container_height
|
||||||
|
- GUAC_TERMINAL_SCROLLBAR_PADDING*2;
|
||||||
|
|
||||||
|
/* Calculate legal delta between scroll values */
|
||||||
|
int scroll_delta;
|
||||||
|
if (scrollbar->max > scrollbar->min)
|
||||||
|
scroll_delta = scrollbar->max - scrollbar->min;
|
||||||
|
else
|
||||||
|
scroll_delta = 0;
|
||||||
|
|
||||||
|
/* Scale handle relative to visible area vs. scrolling region size */
|
||||||
|
int proportional_height = max_handle_height
|
||||||
|
* scrollbar->visible_area
|
||||||
|
/ (scroll_delta + scrollbar->visible_area);
|
||||||
|
|
||||||
|
/* Ensure handle is no smaller than minimum height */
|
||||||
|
if (proportional_height > GUAC_TERMINAL_SCROLLBAR_MIN_HEIGHT)
|
||||||
|
render_state->handle_height = proportional_height;
|
||||||
|
else
|
||||||
|
render_state->handle_height = GUAC_TERMINAL_SCROLLBAR_MIN_HEIGHT;
|
||||||
|
|
||||||
|
/* Ensure handle is no larger than maximum height */
|
||||||
|
if (render_state->handle_height > max_handle_height)
|
||||||
|
render_state->handle_height = max_handle_height;
|
||||||
|
|
||||||
|
/* Calculate handle position */
|
||||||
|
render_state->handle_x = GUAC_TERMINAL_SCROLLBAR_PADDING;
|
||||||
|
|
||||||
|
/* Handle Y position is relative to current scroll value */
|
||||||
|
if (scroll_delta > 0)
|
||||||
|
render_state->handle_y = GUAC_TERMINAL_SCROLLBAR_PADDING
|
||||||
|
+ (max_handle_height - render_state->handle_height)
|
||||||
|
* (scrollbar->value - scrollbar->min)
|
||||||
|
/ scroll_delta;
|
||||||
|
|
||||||
|
/* ... unless there is only one possible scroll value */
|
||||||
|
else
|
||||||
|
render_state->handle_y = GUAC_TERMINAL_SCROLLBAR_PADDING;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void guac_terminal_scrollbar_flush(guac_terminal_scrollbar* scrollbar) {
|
||||||
|
|
||||||
guac_socket* socket = scrollbar->client->socket;
|
guac_socket* socket = scrollbar->client->socket;
|
||||||
|
|
||||||
/* Skip if no scrolling region at all */
|
/* Get old render state */
|
||||||
if (scrollbar->min >= scrollbar->max)
|
guac_terminal_scrollbar_render_state* old_state = &scrollbar->render_state;
|
||||||
return;
|
|
||||||
|
|
||||||
int region_size = scrollbar->max - scrollbar->min;
|
/* Calculate new render state */
|
||||||
|
guac_terminal_scrollbar_render_state new_state;
|
||||||
|
calculate_render_state(scrollbar, &new_state);
|
||||||
|
|
||||||
/* Calculate handle dimensions */
|
/* Reposition container if moved */
|
||||||
int handle_width = GUAC_TERMINAL_SCROLLBAR_WIDTH - GUAC_TERMINAL_SCROLLBAR_PADDING*2;
|
if (old_state->container_x != new_state.container_x
|
||||||
int handle_height = GUAC_TERMINAL_SCROLLBAR_MIN_HEIGHT;
|
|| old_state->container_y != new_state.container_y) {
|
||||||
|
|
||||||
/* Size handle relative to visible area */
|
guac_protocol_send_move(socket,
|
||||||
int padded_container_height = (scrollbar->parent_height - GUAC_TERMINAL_SCROLLBAR_PADDING*2);
|
scrollbar->container, scrollbar->parent,
|
||||||
int proportional_height = padded_container_height * scrollbar->visible_area / (region_size + scrollbar->visible_area);
|
new_state.container_x,
|
||||||
if (proportional_height > handle_height)
|
new_state.container_y,
|
||||||
handle_height = proportional_height;
|
0);
|
||||||
|
|
||||||
/* Calculate handle position and dimensions */
|
}
|
||||||
int handle_x = GUAC_TERMINAL_SCROLLBAR_PADDING;
|
|
||||||
int handle_y = GUAC_TERMINAL_SCROLLBAR_PADDING
|
/* Resize and redraw container if size changed */
|
||||||
+ (padded_container_height - handle_height) * (scrollbar->value - scrollbar->min) / region_size;
|
if (old_state->container_width != new_state.container_width
|
||||||
|
|| old_state->container_height != new_state.container_height) {
|
||||||
|
|
||||||
|
/* Set new size */
|
||||||
|
guac_protocol_send_size(socket, scrollbar->container,
|
||||||
|
new_state.container_width,
|
||||||
|
new_state.container_height);
|
||||||
|
|
||||||
|
/* Fill container with solid color */
|
||||||
|
guac_protocol_send_rect(socket, scrollbar->container, 0, 0,
|
||||||
|
new_state.container_width,
|
||||||
|
new_state.container_height);
|
||||||
|
|
||||||
|
guac_protocol_send_cfill(socket, GUAC_COMP_SRC, scrollbar->container,
|
||||||
|
0x40, 0x40, 0x40, 0xFF);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reposition handle if moved */
|
||||||
|
if (old_state->handle_x != new_state.handle_x
|
||||||
|
|| old_state->handle_y != new_state.handle_y) {
|
||||||
|
|
||||||
/* Reposition handle relative to container and current value */
|
|
||||||
guac_protocol_send_move(socket,
|
guac_protocol_send_move(socket,
|
||||||
scrollbar->handle, scrollbar->container,
|
scrollbar->handle, scrollbar->container,
|
||||||
handle_x, handle_y, 0);
|
new_state.handle_x,
|
||||||
|
new_state.handle_y,
|
||||||
|
0);
|
||||||
|
|
||||||
/* Resize handle relative to scrollable area */
|
}
|
||||||
|
|
||||||
|
/* Resize and redraw handle if size changed */
|
||||||
|
if (old_state->handle_width != new_state.handle_width
|
||||||
|
|| old_state->handle_height != new_state.handle_height) {
|
||||||
|
|
||||||
|
/* Send new size */
|
||||||
guac_protocol_send_size(socket, scrollbar->handle,
|
guac_protocol_send_size(socket, scrollbar->handle,
|
||||||
handle_width, handle_height);
|
new_state.handle_width,
|
||||||
|
new_state.handle_height);
|
||||||
|
|
||||||
|
/* Fill and stroke handle with solid color */
|
||||||
|
guac_protocol_send_rect(socket, scrollbar->handle, 0, 0,
|
||||||
|
new_state.handle_width,
|
||||||
|
new_state.handle_height);
|
||||||
|
|
||||||
/* Fill handle with solid color */
|
|
||||||
guac_protocol_send_rect(socket, scrollbar->handle, 0, 0, handle_width, handle_height);
|
|
||||||
guac_protocol_send_cfill(socket, GUAC_COMP_SRC, scrollbar->handle,
|
guac_protocol_send_cfill(socket, GUAC_COMP_SRC, scrollbar->handle,
|
||||||
0x80, 0x80, 0x80, 0xFF);
|
0x80, 0x80, 0x80, 0xFF);
|
||||||
|
|
||||||
guac_protocol_send_cstroke(socket, GUAC_COMP_OVER, scrollbar->handle,
|
guac_protocol_send_cstroke(socket, GUAC_COMP_OVER, scrollbar->handle,
|
||||||
GUAC_LINE_CAP_SQUARE, GUAC_LINE_JOIN_MITER, 2,
|
GUAC_LINE_CAP_SQUARE, GUAC_LINE_JOIN_MITER, 2,
|
||||||
0xA0, 0xA0, 0xA0, 0xFF);
|
0xA0, 0xA0, 0xA0, 0xFF);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store current render state */
|
||||||
|
scrollbar->render_state = new_state;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_terminal_scrollbar_set_bounds(guac_terminal_scrollbar* scrollbar,
|
void guac_terminal_scrollbar_set_bounds(guac_terminal_scrollbar* scrollbar,
|
||||||
@ -138,9 +252,6 @@ void guac_terminal_scrollbar_set_bounds(guac_terminal_scrollbar* scrollbar,
|
|||||||
scrollbar->min = min;
|
scrollbar->min = min;
|
||||||
scrollbar->max = max;
|
scrollbar->max = max;
|
||||||
|
|
||||||
/* Update handle position and size */
|
|
||||||
__update_handle(scrollbar);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_terminal_scrollbar_set_value(guac_terminal_scrollbar* scrollbar,
|
void guac_terminal_scrollbar_set_value(guac_terminal_scrollbar* scrollbar,
|
||||||
@ -155,44 +266,15 @@ void guac_terminal_scrollbar_set_value(guac_terminal_scrollbar* scrollbar,
|
|||||||
/* Update value */
|
/* Update value */
|
||||||
scrollbar->value = value;
|
scrollbar->value = value;
|
||||||
|
|
||||||
/* Update handle position and size */
|
|
||||||
__update_handle(scrollbar);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_terminal_scrollbar_parent_resized(guac_terminal_scrollbar* scrollbar,
|
void guac_terminal_scrollbar_parent_resized(guac_terminal_scrollbar* scrollbar,
|
||||||
int parent_width, int parent_height, int visible_area) {
|
int parent_width, int parent_height, int visible_area) {
|
||||||
|
|
||||||
guac_socket* socket = scrollbar->client->socket;
|
|
||||||
|
|
||||||
/* Calculate container position and dimensions */
|
|
||||||
int container_x = parent_width - GUAC_TERMINAL_SCROLLBAR_WIDTH;
|
|
||||||
int container_y = 0;
|
|
||||||
int container_width = GUAC_TERMINAL_SCROLLBAR_WIDTH;
|
|
||||||
int container_height = parent_height;
|
|
||||||
|
|
||||||
/* Reposition container relative to parent dimensions */
|
|
||||||
guac_protocol_send_move(socket,
|
|
||||||
scrollbar->container, scrollbar->parent,
|
|
||||||
container_x, container_y, 0);
|
|
||||||
|
|
||||||
/* Resize to fit within parent */
|
|
||||||
guac_protocol_send_size(socket, scrollbar->container,
|
|
||||||
container_width, container_height);
|
|
||||||
|
|
||||||
/* Fill container with solid color */
|
|
||||||
guac_protocol_send_rect(socket, scrollbar->container, 0, 0,
|
|
||||||
container_width, container_height);
|
|
||||||
guac_protocol_send_cfill(socket, GUAC_COMP_SRC, scrollbar->container,
|
|
||||||
0x40, 0x40, 0x40, 0xFF);
|
|
||||||
|
|
||||||
/* Assign new dimensions */
|
/* Assign new dimensions */
|
||||||
scrollbar->parent_width = parent_width;
|
scrollbar->parent_width = parent_width;
|
||||||
scrollbar->parent_height = parent_height;
|
scrollbar->parent_height = parent_height;
|
||||||
scrollbar->visible_area = visible_area;
|
scrollbar->visible_area = visible_area;
|
||||||
|
|
||||||
/* Update handle position and size */
|
|
||||||
__update_handle(scrollbar);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,58 @@
|
|||||||
*/
|
*/
|
||||||
#define GUAC_TERMINAL_SCROLLBAR_MIN_HEIGHT 64
|
#define GUAC_TERMINAL_SCROLLBAR_MIN_HEIGHT 64
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of all scrollbar components, describing all variable aspects of
|
||||||
|
* the scrollbar's appearance.
|
||||||
|
*/
|
||||||
|
typedef struct guac_terminal_scrollbar_render_state {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current X-coordinate of the upper-left corner of the scrollbar's
|
||||||
|
* handle. This value will be relative to the scrollbar's containing layer.
|
||||||
|
*/
|
||||||
|
int handle_x;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current Y-coordinate of the upper-left corner of the scrollbar's
|
||||||
|
* handle. This value will be relative to the scrollbar's containing layer.
|
||||||
|
*/
|
||||||
|
int handle_y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The width of the scrollbar's handle.
|
||||||
|
*/
|
||||||
|
int handle_width;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The height of the scrollbar's handle.
|
||||||
|
*/
|
||||||
|
int handle_height;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current X-coordinate of the upper-left corner of the scrollbar's
|
||||||
|
* containing layer.
|
||||||
|
*/
|
||||||
|
int container_x;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current Y-coordinate of the upper-left corner of the scrollbar's
|
||||||
|
* containing layer.
|
||||||
|
*/
|
||||||
|
int container_y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The width of the scrollbar's containing layer.
|
||||||
|
*/
|
||||||
|
int container_width;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The height of the scrollbar's containing layer.
|
||||||
|
*/
|
||||||
|
int container_height;
|
||||||
|
|
||||||
|
} guac_terminal_scrollbar_render_state;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A scrollbar, made up of a containing layer and inner draggable handle. The
|
* A scrollbar, made up of a containing layer and inner draggable handle. The
|
||||||
* position of the handle within the layer represents the value of the
|
* position of the handle within the layer represents the value of the
|
||||||
@ -102,6 +154,11 @@ typedef struct guac_terminal_scrollbar {
|
|||||||
*/
|
*/
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current state of all variable, visible parts of the scrollbar.
|
||||||
|
*/
|
||||||
|
guac_terminal_scrollbar_render_state render_state;
|
||||||
|
|
||||||
} guac_terminal_scrollbar;
|
} guac_terminal_scrollbar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,6 +202,18 @@ guac_terminal_scrollbar* guac_terminal_scrollbar_alloc(guac_client* client,
|
|||||||
*/
|
*/
|
||||||
void guac_terminal_scrollbar_free(guac_terminal_scrollbar* scrollbar);
|
void guac_terminal_scrollbar_free(guac_terminal_scrollbar* scrollbar);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes the render state of the given scrollbar, updating the remote display
|
||||||
|
* accordingly.
|
||||||
|
*
|
||||||
|
* This may cause instructions to be written to the client's socket, but the
|
||||||
|
* client's socket will not be automatically flushed.
|
||||||
|
*
|
||||||
|
* @param scrollbar
|
||||||
|
* The scrollbar whose render state is to be flushed.
|
||||||
|
*/
|
||||||
|
void guac_terminal_scrollbar_flush(guac_terminal_scrollbar* scrollbar);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the minimum and maximum allowed scroll values of the given scrollbar
|
* Sets the minimum and maximum allowed scroll values of the given scrollbar
|
||||||
* to the given values. If necessary, the current value of the scrollbar will
|
* to the given values. If necessary, the current value of the scrollbar will
|
||||||
|
@ -682,6 +682,8 @@ void guac_terminal_scroll_display_down(guac_terminal* terminal,
|
|||||||
}
|
}
|
||||||
|
|
||||||
guac_terminal_display_flush(terminal->display);
|
guac_terminal_display_flush(terminal->display);
|
||||||
|
guac_terminal_scrollbar_flush(terminal->scrollbar);
|
||||||
|
|
||||||
guac_protocol_send_sync(terminal->client->socket,
|
guac_protocol_send_sync(terminal->client->socket,
|
||||||
terminal->client->last_sent_timestamp);
|
terminal->client->last_sent_timestamp);
|
||||||
guac_socket_flush(terminal->client->socket);
|
guac_socket_flush(terminal->client->socket);
|
||||||
@ -747,6 +749,8 @@ void guac_terminal_scroll_display_up(guac_terminal* terminal,
|
|||||||
}
|
}
|
||||||
|
|
||||||
guac_terminal_display_flush(terminal->display);
|
guac_terminal_display_flush(terminal->display);
|
||||||
|
guac_terminal_scrollbar_flush(terminal->scrollbar);
|
||||||
|
|
||||||
guac_protocol_send_sync(terminal->client->socket,
|
guac_protocol_send_sync(terminal->client->socket,
|
||||||
terminal->client->last_sent_timestamp);
|
terminal->client->last_sent_timestamp);
|
||||||
guac_socket_flush(terminal->client->socket);
|
guac_socket_flush(terminal->client->socket);
|
||||||
@ -1170,6 +1174,7 @@ int guac_terminal_resize(guac_terminal* terminal, int width, int height) {
|
|||||||
|
|
||||||
/* If terminal size hasn't changed, still need to flush */
|
/* If terminal size hasn't changed, still need to flush */
|
||||||
else {
|
else {
|
||||||
|
guac_terminal_scrollbar_flush(terminal->scrollbar);
|
||||||
guac_protocol_send_sync(socket, client->last_sent_timestamp);
|
guac_protocol_send_sync(socket, client->last_sent_timestamp);
|
||||||
guac_socket_flush(socket);
|
guac_socket_flush(socket);
|
||||||
}
|
}
|
||||||
@ -1181,6 +1186,7 @@ int guac_terminal_resize(guac_terminal* terminal, int width, int height) {
|
|||||||
void guac_terminal_flush(guac_terminal* terminal) {
|
void guac_terminal_flush(guac_terminal* terminal) {
|
||||||
guac_terminal_commit_cursor(terminal);
|
guac_terminal_commit_cursor(terminal);
|
||||||
guac_terminal_display_flush(terminal->display);
|
guac_terminal_display_flush(terminal->display);
|
||||||
|
guac_terminal_scrollbar_flush(terminal->scrollbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
void guac_terminal_lock(guac_terminal* terminal) {
|
void guac_terminal_lock(guac_terminal* terminal) {
|
||||||
|
Loading…
Reference in New Issue
Block a user