/** * \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; uint8_t new_position; struct win_area bg1_draw_area; struct win_area bg2_draw_area; struct win_area knob_draw_area; 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; } /* Set up the drawing areas */ knob_draw_area.pos = clip->origin; knob_draw_area.size = area->size; bg1_draw_area = knob_draw_area; win_inflate_area(&bg1_draw_area, -1); bg2_draw_area = bg1_draw_area; /* Draw the slider knob according to configured orientation. */ if (option & WTK_SLIDER_VERTICAL) { knob_draw_area.pos.y += slider->position + 1; knob_draw_area.size.y = WTK_SLIDER_KNOB_WIDTH; bg1_draw_area.size.y = slider->position; bg2_draw_area.pos.y += slider->position + WTK_SLIDER_KNOB_WIDTH; bg2_draw_area.size.y -= slider->position + WTK_SLIDER_KNOB_WIDTH; } else { knob_draw_area.pos.x += slider->position + 1; knob_draw_area.size.x = WTK_SLIDER_KNOB_WIDTH; bg1_draw_area.size.x = slider->position; bg2_draw_area.pos.x += slider->position + WTK_SLIDER_KNOB_WIDTH; bg2_draw_area.size.x -= slider->position + WTK_SLIDER_KNOB_WIDTH; } /* Draw slider knob border. */ gfx_draw_rect(knob_draw_area.pos.x, knob_draw_area.pos.y, knob_draw_area.size.x, knob_draw_area.size.y, WTK_SLIDER_BORDER_COLOR); /* Shrink knob area for the graphical fill */ win_inflate_area(&knob_draw_area, -1); /* Draw slider knob. */ gfx_draw_filled_rect(knob_draw_area.pos.x, knob_draw_area.pos.y, knob_draw_area.size.x, knob_draw_area.size.y, knob_color); /* Draw left/top section of background around the slider */ gfx_draw_filled_rect(bg1_draw_area.pos.x, bg1_draw_area.pos.y, bg1_draw_area.size.x, bg1_draw_area.size.y, WTK_SLIDER_BACKGROUND_COLOR); /* Draw right/bottom section of background around the slider */ gfx_draw_filled_rect(bg2_draw_area.pos.x, bg2_draw_area.pos.y, bg2_draw_area.size.x, bg2_draw_area.size.y, WTK_SLIDER_BACKGROUND_COLOR); /* Draw slider frame border. */ gfx_draw_rect(clip->origin.x, clip->origin.y, area->size.x, area->size.y, WTK_SLIDER_BORDER_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. */ new_position = wtk_rescale_value( value, slider->maximum, length); if (option & WTK_SLIDER_CMD_MOVE) { send_command = true; } if (new_position != slider->position) { slider->position = new_position; 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 activity 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 Setup widget demo * * Allocates memory for the application context, and creates all widgets that * make up its interface. If memory allocation or widget creation fails, the * application exits immediately. * * \return Boolean true if the application was launched successfully, false if * a memory allocation occurred. */ bool app_widget_launch(void) { struct win_window *parent; struct win_area area; struct wtk_button *button; /* Create a new context for the GUI */ widget_ctx = membag_alloc(sizeof(struct widget_context)); if (!widget_ctx) { return false; } /* Initialize context data. */ widget_ctx->frame_bg.type = GFX_BITMAP_SOLID; widget_ctx->frame_bg.data.color = GFX_COLOR(220, 220, 220); /* Set the area for the GUI window */ area = win_get_attributes(win_get_root())->area; win_inflate_area(&area, -20); /* Create and show the main GUI frame */ widget_ctx->frame = wtk_basic_frame_create( win_get_root(), &area, &widget_ctx->frame_bg, NULL, widget_frame_command_handler, widget_ctx); if (!widget_ctx->frame) { goto error_frame; } parent = wtk_basic_frame_as_child(widget_ctx->frame); win_show(parent); /* Set the background information for the plot widget */ widget_ctx->plot_bg.type = GFX_BITMAP_SOLID; widget_ctx->plot_bg.data.color = GFX_COLOR_WHITE; /* Adjust area for the plot widget */ area.size.y -= 80; area.size.x -= 40; /* Create and show the plot widget with vertical axis marks */ widget_ctx->plot = wtk_plot_create(parent, &area, 100, 10, GFX_COLOR_RED, &widget_ctx->plot_bg, WTK_PLOT_LEFT_TO_RIGHT); if (!widget_ctx->plot) { goto error_widget; } wtk_plot_set_grid(widget_ctx->plot, WTK_PLOT_TICKS_VERTICAL, 10, 0, 10, 0, GFX_COLOR_BLUE, GFX_COLOR_GREEN); win_show(wtk_plot_as_child(widget_ctx->plot)); /* Adjust area for the slider widget */ area.pos.y += area.size.y + 10; area.size.y = 40; area.size.x -= 60; /* Create and show the slider widget */ widget_ctx->slider = wtk_slider_create(parent, &area, 100, 0, WTK_SLIDER_HORIZONTAL, (win_command_t)SLIDER_ID); if (!widget_ctx->slider) { goto error_widget; } win_show(wtk_slider_as_child(widget_ctx->slider)); /* Adjust area for the button widget */ area.pos.x += area.size.x + 10; area.size.x = 50; /* Create and show the button widget */ button = wtk_button_create(parent, &area, "Add", (win_command_t)BUTTON_ID); if (!button) { goto error_widget; } win_show(wtk_button_as_child(button)); return true; /* Error handling to clean up allocations after an error */ error_widget: win_destroy(wtk_basic_frame_as_child(widget_ctx->frame)); error_frame: membag_free(widget_ctx); return false; }
/** * \brief Setup widget demo * * Allocates memory for the application context, and creates all widgets that * make up its interface. If memory allocation or widget creation fails, the * application exits immediately. * * \return Boolean true if the application was launched successfully, false if * a memory allocation occurred. */ bool app_widget_launch(void) { struct win_window *parent; struct win_area area; struct wtk_check_box *cb; struct wtk_radio_group *rg; struct wtk_radio_button *rb; struct wtk_button *btn; /* Create a new context for the GUI */ widget_ctx = membag_alloc(sizeof(struct widget_context)); if (!widget_ctx) { return false; } /* Initialize context data. */ widget_ctx->color_scheme = 0; widget_ctx->color_invert = 0; /* Set the background information for the GUI window */ widget_ctx->frame_bg.type = GFX_BITMAP_SOLID; widget_ctx->frame_bg.data.color = APP_BACKGROUND_COLOR; /* Set the area for the GUI window */ area = win_get_attributes(win_get_root())->area; win_inflate_area(&area, -20); /* Create and show the main GUI frame */ widget_ctx->frame = wtk_basic_frame_create( win_get_root(), &area, &widget_ctx->frame_bg, NULL, widget_frame_command_handler, widget_ctx); if (!widget_ctx->frame) { goto error_frame; } parent = wtk_basic_frame_as_child(widget_ctx->frame); win_show(parent); /* Update area for the slider widget */ area.pos.x = WIDGET_POS_X; area.pos.y = WIDGET_POS_Y + SLIDER_POS_Y; area.size.x = SLIDER_SIZE_X; area.size.y = SLIDER_SIZE_Y; /* Create slider inside frame */ widget_ctx->slider = wtk_slider_create(parent, &area, 100, 50, WTK_SLIDER_HORIZONTAL | WTK_SLIDER_CMD_MOVE | WTK_SLIDER_CMD_RELEASE, (win_command_t)SLIDER_ID); if (!widget_ctx->slider) { goto error_widget; } win_show(wtk_slider_as_child(widget_ctx->slider)); /* Update area for the progress bar widget */ area.pos.x += area.size.x + SLIDER_PB_SPACING_X; area.size.x = PB_SIZE_X; area.size.y = PB_SIZE_Y; /* Create progress bar to the right of the slider */ widget_ctx->pb = wtk_progress_bar_create(parent, &area, 100, 50, GFX_COLOR_BLACK, GFX_COLOR_BLACK, WTK_PROGRESS_BAR_HORIZONTAL); if (!widget_ctx->pb) { goto error_widget; } win_show(wtk_progress_bar_as_child(widget_ctx->pb)); app_widget_update_colors(widget_ctx); /* Update area for the checkbox widget */ area.pos.x = WIDGET_POS_X; area.pos.y += area.size.y + CHECK_BOX_SPACING_Y; wtk_check_box_size_hint(&area.size, checkbox_string); /* Create check box below slider and progress bar */ cb = wtk_check_box_create(parent, &area, checkbox_string, false, (win_command_t)CHECK_BOX_ID); if (!cb) { goto error_widget; } win_show(wtk_check_box_as_child(cb)); /* Create a logical group for the radio buttons */ rg = wtk_radio_group_create(); if (!rg) { goto error_widget; } /* Update area for the first radio button widget */ area.pos.y += area.size.y + RADIO_BUTTON_SPACING_Y; wtk_radio_button_size_hint(&area.size, rb1_string); /* Create first radio button widget */ rb = wtk_radio_button_create(parent, &area, rb1_string, true, rg, (win_command_t)RADIO_BUTTON_1_ID); if (!rb) { goto error_widget; } win_show(wtk_radio_button_as_child(rb)); /* Update area for the second radio button widget */ area.pos.y += area.size.y + RADIO_BUTTON_SPACING_Y; wtk_radio_button_size_hint(&area.size, rb2_string); /* Create second radio button widget */ rb = wtk_radio_button_create(parent, &area, rb2_string, false, rg, (win_command_t)RADIO_BUTTON_2_ID); if (!rb) { goto error_widget; } win_show(wtk_radio_button_as_child(rb)); /* Update area for the button widget */ area.pos.y += area.size.y + BUTTON_SPACING_Y; area.size.x = SLIDER_SIZE_X + SLIDER_PB_SPACING_X + PB_SIZE_X; area.size.y = BUTTON_SIZE_Y; /* Create button widget */ btn = wtk_button_create(parent, &area, btn_string, (win_command_t)BUTTON_ID); wtk_button_size_hint(&area.size, btn_string); if (!btn) { goto error_widget; } win_show(wtk_button_as_child(btn)); return true; /* Error handling to clean up allocations after an error */ error_widget: win_destroy(wtk_basic_frame_as_child(widget_ctx->frame)); error_frame: membag_free(widget_ctx); return false; }