void wtk_plot_set_grid(struct wtk_plot *plot, uint8_t axis_option, uint8_t axis_spacing_x, uint8_t axis_offset_x, uint8_t axis_spacing_y, uint8_t axis_offset_y, gfx_color_t axis_color, gfx_color_t axis_zero_color) { Assert(plot); uint8_t height; struct win_area const *area; area = win_get_area(plot->container); /* Makes grid/axis fit inside the window border */ height = area->size.y - 2; plot->axis_option = axis_option; plot->axis_spacing_x = axis_spacing_x; plot->axis_offset_x = axis_offset_x; plot->axis_spacing_y = wtk_rescale_value(axis_spacing_y, plot->maximum, height); plot->axis_offset_y = height - wtk_rescale_value(axis_offset_y, plot->maximum, height); plot->axis_color = axis_color; plot->axis_zero_color = axis_zero_color; }
/** * \brief Add a value to the end of the plot. * * Scales the input value to fit the plot dimensions and adds it to the end of * the ring buffer. * * \param plot Pointer to wtk_plot struct to set new value for. * \param value New value for the plot. * * \return True. */ bool wtk_plot_add_value(struct wtk_plot *plot, uint8_t value) { uint8_t height; uint8_t maximum; struct win_area const *area; Assert(plot); Assert(value <= plot->maximum); Assert(plot->buffer_start < plot->num_datapoints); maximum = plot->maximum; area = win_get_area(plot->container); /* Makes the plot fit inside the window border. */ height = area->size.y - 2; /* Rescales the added value to fit inside the plot */ /* and stores it in the ring buffer. */ *(plot->plot_buffer + plot->buffer_start) = height - wtk_rescale_value((value), maximum, height - 1); /* Increments ring buffer pointer and resets at end */ plot->buffer_start++; if (plot->buffer_start >= plot->num_datapoints) { plot->buffer_start = 0; } return true; }
/** * \brief Set new slider value. * * Updates the current value and issues a redrawing of the slider if its value * was indeed changed. In this case, the new position of the slider knob is * also computed. * * \param slider Pointer to wtk_slider struct to set new value for. * \param value New value for the slider. * * \return True if slider value was changed. */ bool wtk_slider_set_value(struct wtk_slider *slider, uint8_t value) { struct win_area const *area; uint8_t length; uint8_t option; assert(slider); assert(value <= slider->maximum); option = slider->option; if (option & WTK_SLIDER_INVERT) { value = slider->maximum - value; } if (slider->value != value) { slider->value = value; area = win_get_area(slider->container); /* Get length of slider from window. * wtk_slider_create() asserts that this is an 8-bit value. */ if (option & WTK_SLIDER_VERTICAL) { length = area->size.y; } else { length = area->size.x; } length -= WTK_SLIDER_KNOB_WIDTH; slider->position = wtk_rescale_value(value, slider->maximum, length); win_redraw(slider->container); return true; } else { return false; } }
/** * \brief Set new progress bar value. * * Updates the current value and issues a redrawing of the progress bar if its * value was indeed changed. In this case, a new end position for the progress * bar's fill area is also computed. * * \param bar Pointer to wtk_progress_bar struct to set new value for. * \param value New value for the progress bar. * * \return True if progress bar's value was changed. */ bool wtk_progress_bar_set_value(struct wtk_progress_bar *bar, uint8_t value) { uint8_t length; uint8_t option; uint8_t maximum; struct win_area const *area; Assert(bar); Assert(value <= bar->maximum); if (value != bar->value) { bar->value = value; option = bar->option; maximum = bar->maximum; area = win_get_area(bar->container); /* Get length of progress bar from window. */ if (option & WTK_PROGRESS_BAR_VERTICAL) { length = area->size.y; } else { length = area->size.x; } length -= 2; if (option & WTK_PROGRESS_BAR_INVERT) { value = maximum - value; } bar->position = wtk_rescale_value(value, maximum, length); win_redraw(bar->container); return true; } else { return false; } }
/** * \brief Create a new slider widget. * * Allocates the necessary memory and intializes the window and data for slider * widgets. If there is not enough memory, the function returns NULL.\n * To destroy a slider widget and all its contents, and free its memory, call * \ref win_destroy() on the slider's child reference, given by * \ref wtk_slider_as_child(), like this: * "win_destroy(wtk_slider_as_child(my_slider_ptr));".\par * * Slider widgets fill the specified area and perform a mapping of the slider * knob's position to a value between 0 and maximum. The length of the slider * cannot exceed 255 pixels.\par * * By default, the value 0 corresponds to the top-most position for a vertical * slider, and the left-most position for a horizontal one. The slider's * orientation and inversion of the value can be configured.\par * * A slider can be configured to issue command events whenever its value is * changed by a pointer and/or when a pointer releases it.\par * * Refer to <gfx/wtk.h> for available configuration options. * * \todo Revisit, support larger sliders and values given a config symbol. * * \param parent Pointer to parent win_window struct. * \param area Pointer to win_area struct with position and size of the slider. * \param maximum Maximum value of the slider. * \param value Initial value of the slider. * \param option Configuration options for slider. * See \ref gfx_wtk_slider_options * \param command Command to send to parent window. Must be non-zero if used. * * \return Pointer to new slider, if memory allocation was successful. */ struct wtk_slider *wtk_slider_create(struct win_window *parent, struct win_area const *area, uint8_t maximum, uint8_t value, uint8_t option, win_command_t command) { struct win_attributes attr; struct wtk_slider *slider; uint8_t length; // Do sanity check on parameters. assert(maximum > 0); assert(area); assert(parent); // Allocate memory for the control data. slider = membag_alloc(sizeof(struct wtk_slider)); if (!slider) { goto outofmem_slider; } // Initialize the slider data. slider->state = WTK_SLIDER_NORMAL; slider->maximum = maximum; slider->value = value; slider->option = option; // Invert the initial value if slider is inverted. if (option & WTK_SLIDER_INVERT) { value = maximum - value; } slider->value = value; // Enforce a non-zero command value, if these are enabled. if (option & (WTK_SLIDER_CMD_MOVE | WTK_SLIDER_CMD_RELEASE)) { assert(command > 0); slider->command = command; } // Set up event handling for the widget window. attr.event_handler = wtk_slider_handler; attr.custom = slider; // Do a sanity check of the specified window area parameters. attr.area = *area; assert(attr.area.size.x > 0); assert(attr.area.size.y > 0); if (option & WTK_SLIDER_VERTICAL) { assert(attr.area.size.x > 3); assert(attr.area.size.x <= (uint8_t) ~ 0); assert(attr.area.size.y > WTK_SLIDER_KNOB_WIDTH); length = attr.area.size.y; } else { assert(attr.area.size.x > WTK_SLIDER_KNOB_WIDTH); assert(attr.area.size.y <= (uint8_t) ~ 0); assert(attr.area.size.y > 3); length = attr.area.size.x; } // Set slider knob position according to initial value. length -= WTK_SLIDER_KNOB_WIDTH; slider->position = wtk_rescale_value(value, maximum, length); /* All drawing is done in wtk_slider_handler() to reduce overhead. * Slider has no transparent areas, so parent does not need redrawing. */ attr.background = NULL; attr.behavior = 0; // Create the widget window. slider->container = win_create(parent, &attr); if (!slider->container) { goto outofmem_container; } // Store absolute position win_translate_win_to_root(slider->container, &slider->root_pos); return slider; outofmem_container: membag_free(slider); outofmem_slider: return NULL; }
/** * \brief Slider event handler. * * This is the window event handler for slider widgets. It handles the three * relevant event types sent to a slider's container window, i.e., drawing, * pointer and destroy events.\par * * For POINTER events, the slider value is only updated when the pointer moves, * and not upon press or release events. The handler will grab the pointer and * allow for it to move outside the actual widget window and still control the * slider knob. * * \param win Window receiving the event. * \param type The event type. * \param data Custom data, depending on event type. * * \return True if the event was recognized and accepted. */ static bool wtk_slider_handler(struct win_window *win, enum win_event_type type, void const *data) { struct win_pointer_event const *event; struct win_clip_region const *clip; struct win_command_event command; struct win_area const *area; struct wtk_slider *slider; struct win_point origin; gfx_color_t knob_color; gfx_coord_t position; gfx_coord_t length; uint8_t option; uint8_t value; bool send_command; slider = (struct wtk_slider *)win_get_custom_data(win); // There should not be other windows in this widget. assert(win == slider->container); switch (type) { case WIN_EVENT_DRAW: /* For DRAW events, the data parameter points to the * clipping region. */ clip = (struct win_clip_region const *)data; area = win_get_area(win); option = slider->option; if (slider->state == WTK_SLIDER_NORMAL) { knob_color = WTK_SLIDER_KNOB_COLOR_NORMAL; } else { knob_color = WTK_SLIDER_KNOB_COLOR_MOVING; } // Draw slider frame border. gfx_draw_rect(clip->origin.x, clip->origin.y, area->size.x, area->size.y, WTK_SLIDER_BORDER_COLOR); // Draw slider frame background within frame. gfx_draw_filled_rect(clip->origin.x + 1, clip->origin.y + 1, area->size.x - 2, area->size.y - 2, WTK_SLIDER_BACKGROUND_COLOR); // Draw the slider knob according to configured orientation. if (option & WTK_SLIDER_VERTICAL) { // Draw slider knob border. gfx_draw_rect(clip->origin.x, clip->origin.y + slider->position, area->size.x, WTK_SLIDER_KNOB_WIDTH, WTK_SLIDER_BORDER_COLOR); // Draw slider knob. gfx_draw_filled_rect(clip->origin.x + 1, clip->origin.y + slider->position + 1, area->size.x - 2, WTK_SLIDER_KNOB_WIDTH - 2, knob_color); } else { gfx_draw_rect(clip->origin.x + slider->position, clip->origin.y, WTK_SLIDER_KNOB_WIDTH, area->size.y, WTK_SLIDER_BORDER_COLOR); gfx_draw_filled_rect(clip->origin.x + slider->position + 1, clip->origin.y + 1, WTK_SLIDER_KNOB_WIDTH - 2, area->size.y - 2, knob_color); } /* Always accept DRAW events, as the return value is ignored * anyway for that event type. */ return true; case WIN_EVENT_POINTER: /* For POINTER events, the data parameter points to the pointer * event information. */ event = (struct win_pointer_event const *)data; area = win_get_area(win); option = slider->option; send_command = false; origin = slider->root_pos; switch (event->type) { case WIN_POINTER_PRESS: /* Grab the pointer and redraw a highlighted slider. * Slider value is not updated yet. */ if (slider->state == WTK_SLIDER_NORMAL) { slider->state = WTK_SLIDER_MOVING; win_grab_pointer(win); win_redraw(win); }; #if WTK_SLIDER_PARENT_MOVE_SUPPORT // Update root position. win_translate_win_to_root(win, &slider->root_pos); #endif break; case WIN_POINTER_MOVE: /* The slider is only updated when the pointer moves * and the window was previously pressed. The pointer * does not need to remain within the window to control * the knob as only the position along the slider's * direction of orientation is used. */ if (slider->state == WTK_SLIDER_MOVING) { /* Get the position of the pointer relative to * slider knob's origin, and the length of the * slider itself. */ if (option & WTK_SLIDER_VERTICAL) { position = event->pos.y - origin.y; length = area->size.y; } else { position = event->pos.x - origin.x; length = area->size.x; } // Subtract offsets due to slider knob size. position -= WTK_SLIDER_KNOB_WIDTH / 2; length -= WTK_SLIDER_KNOB_WIDTH; /* Bound the value if pointer is outside window. * Otherwise, compute the slider value from the * knob position. */ if (position < 0) { value = 0; } else if (position > length) { value = slider->maximum; } else { value = wtk_rescale_value(position, length, slider->maximum); } // Update slider only if this is a new value. if (slider->value != value) { slider->value = value; /* Compute knob position from value to * get correct positioning. */ slider->position = wtk_rescale_value(value, slider->maximum, length); if (option & WTK_SLIDER_CMD_MOVE) { send_command = true; } win_redraw(win); } } break; case WIN_POINTER_RELEASE: /* Release the pointer and redraw a normal slider. * The slider's value is not updated. Hence, a pointer * press directly followed by a release will leave the * slider value unchanged. */ if (slider->state == WTK_SLIDER_MOVING) { slider->state = WTK_SLIDER_NORMAL; win_grab_pointer(NULL); win_redraw(win); if (option & WTK_SLIDER_CMD_RELEASE) { send_command = true; } } break; default: break; } // Prepare and send command, if it was flagged. if (send_command) { command.sender = slider->container; command.recipient = slider->container; command.data = slider->command; win_queue_command_event(&command); } /* Accept all POINTER events since all acitivity inside the * widget window is relevant. */ return true; case WIN_EVENT_DESTROY: /* Memory allocated for windows will be automatically destroyed * by the window system. We must destroy other allocations. */ membag_free(slider); /* Always accept DESTROY events, as the return value is ignored * anyway for that event type. */ return true; default: // Reject unknown event types. return false; } }
/** * \brief Create a new progress bar widget. * * Allocates the necessary memory and intializes the window and data for * progress bar widgets. If there is not enough memory, the function returns * NULL.\n To destroy a progress bar widget and all its contents, and free its * memory, call \ref win_destroy() on the progress bar's child reference, given * by \ref wtk_progress_bar_as_child(), like this: * "win_destroy(wtk_progress_bar_as_child(my_progress_bar_ptr));".\par * * Progress bar widgets divide their window area in two non-overlapping * rectangles: one with a fill color, and one with a background color. * The ratio between the two rectangles' sizes is given by the progress bar's * value relative to its maximum: a higher value gives a larger fill.\par * * By default, a vertically oriented progress bar fills from the top, while a * horizontal one fills from the left. The progress bar's orientation and fill * direction can both be configured at the time of creation. The fill and * background colors can be changed at runtime.\par * * Refer to <gfx/wtk.h> for available configuration options. * * \param parent Pointer to parent win_window struct. * \param area Pointer to win_area struct with position and size of the * progress bar. Minimum size in both x and y direction is 3 pixels. * \param maximum Maximum value of the progress bar. * \param value Initial value of the progress bar. * \param fill_color Color for filled area. * \param background_color Color for background area. * \param option Configuration options for progress bar. * * \return Pointer to new progress bar, if memory allocation was successful. */ struct wtk_progress_bar *wtk_progress_bar_create(struct win_window *parent, struct win_area const *area, uint8_t maximum, uint8_t value, gfx_color_t fill_color, gfx_color_t background_color, uint8_t option) { uint8_t length; /* Do sanity check on parameters. */ Assert(maximum > 0); Assert(value <= maximum); Assert(area); Assert(parent); /* Attributes scratchpad. */ struct win_attributes attr; /* Allocate memory for the control data. */ struct wtk_progress_bar *bar = membag_alloc(sizeof(struct wtk_progress_bar)); if (!bar) { goto outofmem_bar; } /* Initialize the progress bar data. */ bar->maximum = maximum; bar->value = value; bar->option = option; /* Set the progress bar's colors and prepare the value for computation * of the bar's end position according to the invert option. */ if (option & WTK_PROGRESS_BAR_INVERT) { bar->fill_color = background_color; bar->background_color = fill_color; value = maximum - value; } else { bar->fill_color = fill_color; bar->background_color = background_color; } /* Set up handling information. */ attr.event_handler = wtk_progress_bar_handler; attr.custom = bar; /* Do sanity check of specified window area parameters * according to the orientation of the progress bar. */ attr.area = *area; Assert(attr.area.size.x > 3); Assert(attr.area.size.y > 3); if (option & WTK_PROGRESS_BAR_VERTICAL) { Assert(attr.area.size.y < (uint8_t) ~0); length = attr.area.size.y; } else { Assert(attr.area.size.x < (uint8_t) ~0); length = attr.area.size.x; } length -= 2; /* Set the progress bar's end position. */ bar->position = wtk_rescale_value(value, maximum, length); /* All drawing is done in wtk_progress_bar_handler() so no background is * needed. */ attr.background = NULL; /* Since the widget has no transparent areas, the parent does not need * to be redrawn. */ attr.behavior = 0; /* Create a new window for the progress bar. */ bar->container = win_create(parent, &attr); if (!bar->container) { goto outofmem_container; } return bar; outofmem_container: membag_free(bar); outofmem_bar: return NULL; }