/* Function: wz_ask_parent_to_focus_next Asks the parent to focus the next child if possible */ void wz_ask_parent_to_focus_next(WZ_WIDGET* wgt) { WZ_WIDGET* child; if(wgt->parent == 0) return; child = wgt->next_sib; while(child) { if(wz_ask_parent_for_focus(child)) return; child = child->next_sib; } child = wgt->parent->first_child; while(child != wgt) { if(wz_ask_parent_for_focus(child)) return; child = child->next_sib; } }
/* Function: wz_ask_parent_to_focus_prev Asks the parent to focus the previous child if possible */ void wz_ask_parent_to_focus_prev(WZ_WIDGET* wgt) { WZ_WIDGET* child; if(wgt->parent == 0) return; child = wgt->prev_sib; while(child) { if(wz_ask_parent_for_focus(child)) return; child = child->prev_sib; } child = wgt->parent->last_child; while(child != wgt) { if(wz_ask_parent_for_focus(child)) return; child = child->prev_sib; } }
/* Function: wz_ask_parent_for_focus Asks the parent to defocus everyone but the widget that calls this function. Returns: 1 if the widget already has focus, or succesfully obtained the focus 0 if the widget cannot be focused */ int wz_ask_parent_for_focus(WZ_WIDGET* wgt) { if(wgt->flags & WZ_STATE_HAS_FOCUS) return 1; if(wgt->flags & WZ_STATE_NOTWANT_FOCUS) return 0; if(wgt->flags & WZ_STATE_DISABLED) return 0; if(wgt->flags & WZ_STATE_HIDDEN) return 0; if(wgt->parent == 0) { ALLEGRO_EVENT event; wz_craft_event(&event, WZ_TAKE_FOCUS, wgt, 0); wz_send_event(wgt, &event); } else { ALLEGRO_EVENT event; if(!(wgt->parent->flags & WZ_STATE_HAS_FOCUS)) { wz_ask_parent_for_focus(wgt->parent); } wz_craft_event(&event, WZ_WANT_FOCUS, wgt, 0); wz_send_event(wgt->parent, &event); } return 1; }
/* Function: wz_focus Focuses/defocuses the widget tree. This function propagates the focus down the tree. Parameters: focus - pass 1 to focus the widget tree, or 0 to defocus it */ void wz_focus(WZ_WIDGET* wgt, int focus) { if (focus) { wz_ask_parent_for_focus(wgt); } else { ALLEGRO_EVENT event; wz_craft_event(&event, WZ_LOSE_FOCUS, 0, 0); wz_broadcast_event(wgt, &event); } }
/* Function: wz_init_widget Initializes a generic widget to some sane values */ void wz_init_widget(WZ_WIDGET* wgt, WZ_WIDGET* parent, float x, float y, float w, float h, int id) { wgt->x = x; wgt->y = y; wgt->h = h; wgt->w = w; wgt->flags = 1; wgt->theme = (WZ_THEME*) & wz_def_theme; wgt->proc = wz_widget_proc; wgt->parent = 0; wgt->last_child = 0; wgt->first_child = 0; wgt->next_sib = 0; wgt->prev_sib = 0; wgt->id = id; if (wgt->id == -1) { if (parent == 0) wgt->id = 0; else if (parent->last_child != 0) { wgt->id = parent->last_child->id + 1; } else { wgt->id = parent->id + 1; } } wgt->source = malloc(sizeof(ALLEGRO_EVENT_SOURCE)); al_init_user_event_source(wgt->source); wgt->shortcut.modifiers = 0; wgt->shortcut.keycode = -1; wz_attach(wgt, parent); wz_ask_parent_for_focus(wgt); }
/* Title: Button Section: Internal Function: wz_button_proc See also: <wz_widget_proc> */ int wz_button_proc(WZ_WIDGET* wgt, const ALLEGRO_EVENT* event) { int ret = 1; WZ_BUTTON* but = (WZ_BUTTON*)wgt; float x, y; switch(event->type) { case WZ_LOSE_FOCUS: { ret = 0; break; } case WZ_DRAW: { if(wgt->flags & WZ_STATE_HIDDEN) { ret = 0; } else { int flags = 0; if(wgt->flags & WZ_STATE_DISABLED) flags |= WZ_STYLE_DISABLED; else if (wgt->flags & WZ_STATE_HAS_FOCUS) flags |= WZ_STYLE_FOCUSED; if(but->down) flags |= WZ_STYLE_DOWN; wgt->theme->draw_button(wgt->theme, wgt->local_x, wgt->local_y, wgt->w, wgt->h, but->text, flags); } break; } #if (ALLEGRO_SUB_VERSION > 0) case ALLEGRO_EVENT_TOUCH_MOVE: x = event->touch.x; y = event->touch.y; #endif case ALLEGRO_EVENT_MOUSE_AXES: { if(event->type == ALLEGRO_EVENT_MOUSE_AXES) { x = event->mouse.x; y = event->mouse.y; } if(wgt->flags & WZ_STATE_DISABLED) { ret = 0; } else if((event->mouse.dx != 0 || event->mouse.dy != 0) && wz_widget_rect_test(wgt, x, y) && !(wgt->flags & WZ_STATE_HAS_FOCUS)) { wz_ask_parent_for_focus(wgt); } else { ret = 0; } break; } #if (ALLEGRO_SUB_VERSION > 0) case ALLEGRO_EVENT_TOUCH_BEGIN: x = event->touch.x; y = event->touch.y; #endif case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN: { if(event->type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) { x = event->mouse.x; y = event->mouse.y; } if(wgt->flags & WZ_STATE_DISABLED) { ret = 0; } else if(wz_widget_rect_test(wgt, x, y)) { wz_ask_parent_for_focus(wgt); but->down = 1; wgt->hold_focus = 1; } else ret = 0; break; } case ALLEGRO_EVENT_KEY_DOWN: { switch(event->keyboard.keycode) { case ALLEGRO_KEY_ENTER: { if(wgt->flags & WZ_STATE_DISABLED) { ret = 0; } else if(wgt->flags & WZ_STATE_HAS_FOCUS) { but->down = 1; } else ret = 0; break; } default: ret = 0; } break; } case ALLEGRO_EVENT_KEY_UP: { switch(event->keyboard.keycode) { case ALLEGRO_KEY_ENTER: { if(wgt->flags & WZ_STATE_DISABLED) { ret = 0; } else if(wgt->flags & WZ_STATE_HAS_FOCUS) { wz_trigger(wgt); } else ret = 0; break; } default: ret = 0; } break; } case WZ_HANDLE_SHORTCUT: { if(wgt->flags & WZ_STATE_DISABLED) { ret = 0; } else { if(!(wgt->flags & WZ_STATE_HAS_FOCUS)) { wz_ask_parent_for_focus(wgt); } wz_trigger(wgt); } break; } #if (ALLEGRO_SUB_VERSION > 0) case ALLEGRO_EVENT_TOUCH_END: x = event->touch.x; y = event->touch.y; #endif case ALLEGRO_EVENT_MOUSE_BUTTON_UP: { if(event->type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) { x = event->mouse.x; y = event->mouse.y; } if(wgt->flags & WZ_STATE_DISABLED) { ret = 0; } else if(but->down == 1) { if(wz_widget_rect_test(wgt, x, y)) wz_trigger(wgt); but->down = 0; wgt->hold_focus = 0; } else { ret = 0; } break; } case WZ_DESTROY: { if(but->own) al_ustr_free(but->text); ret = 0; break; } case WZ_SET_TEXT: { if(but->own) { al_ustr_free(but->text); but->text = al_ustr_dup((ALLEGRO_USTR*)event->user.data3); } else { but->text = (ALLEGRO_USTR*)event->user.data3; } break; } case WZ_TRIGGER: { ALLEGRO_EVENT ev; but->down = 0; wz_craft_event(&ev, WZ_BUTTON_PRESSED, wgt, 0); al_emit_user_event(wgt->source, &ev, 0); break; } default: ret = 0; } if(ret == 0) ret = wz_widget_proc(wgt, event); return ret; }
/* Title: Edit Box Section: Internal Function: wz_editbox_proc See also: <wz_widget_proc> */ int wz_editbox_proc(WZ_WIDGET* wgt, ALLEGRO_EVENT* event) { int ret = 1; WZ_EDITBOX* box = (WZ_EDITBOX*)wgt; switch (event->type) { case WZ_DRAW: { if (wgt->flags & WZ_STATE_HIDDEN) { ret = 0; } else { int size = al_ustr_size(box->text); int scroll_offset = al_ustr_offset(box->text, box->scroll_pos); ALLEGRO_USTR_INFO info; ALLEGRO_USTR* text = al_ref_ustr(&info, box->text, scroll_offset, size); int pos = box->cursor_pos - box->scroll_pos; int flags = 0; if(wgt->flags & WZ_STATE_DISABLED) flags = WZ_STYLE_DISABLED; else if(wgt->flags & WZ_STATE_HAS_FOCUS) flags = WZ_STYLE_FOCUSED; wgt->theme->draw_editbox(wgt->theme, wgt->local_x, wgt->local_y, wgt->w, wgt->h, pos, text, flags); } break; } case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN: { if (wgt->flags & WZ_STATE_DISABLED) { ret = 0; } else if (event->mouse.button == 1 && wz_widget_rect_test(wgt, event->mouse.x, event->mouse.y)) { int len = al_ustr_length(box->text); ALLEGRO_USTR_INFO info; ALLEGRO_USTR* text = al_ref_ustr(&info, box->text, box->scroll_pos, len - 1); ALLEGRO_FONT* font = wgt->theme->get_font(wgt->theme, 0); wz_ask_parent_for_focus(wgt); box->cursor_pos = wz_get_text_pos(font, text, event->mouse.x - wgt->x) + box->scroll_pos; } else ret = 0; break; } #if (ALLEGRO_SUB_VERSION > 0) case ALLEGRO_EVENT_TOUCH_BEGIN: { if (wgt->flags & WZ_STATE_DISABLED) { ret = 0; } else if (wz_widget_rect_test(wgt, event->touch.x, event->touch.y)) { int len = al_ustr_length(box->text); ALLEGRO_USTR_INFO info; ALLEGRO_USTR* text = al_ref_ustr(&info, box->text, box->scroll_pos, len - 1); ALLEGRO_FONT* font = wgt->theme->get_font(wgt->theme, 0); wz_ask_parent_for_focus(wgt); box->cursor_pos = wz_get_text_pos(font, text, event->touch.x - wgt->x) + box->scroll_pos; } else ret = 0; break; } #endif case WZ_HANDLE_SHORTCUT: { wz_ask_parent_for_focus(wgt); break; } case WZ_DESTROY: { if(box->own) al_ustr_free(box->text); ret = 0; break; } case ALLEGRO_EVENT_KEY_CHAR: { int len; if(wgt->flags & WZ_STATE_DISABLED || !(wgt->flags & WZ_STATE_HAS_FOCUS)) { ret = 0; break; } else if(event->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL || event->keyboard.modifiers & ALLEGRO_KEYMOD_ALT) { ret = 0; } len = al_ustr_length(box->text); if((int)(event->keyboard.unichar) > 31 && (int)(event->keyboard.unichar) != 127) { al_ustr_insert_chr(box->text, al_ustr_offset(box->text, box->cursor_pos), event->keyboard.unichar); box->cursor_pos++; } else { switch (event->keyboard.keycode) { case ALLEGRO_KEY_BACKSPACE: { if (len > 0 && box->cursor_pos > 0) { al_ustr_remove_chr(box->text, al_ustr_offset(box->text, box->cursor_pos - 1)); box->cursor_pos--; } break; } case ALLEGRO_KEY_DELETE: { if (len > 0 && box->cursor_pos < len) { al_ustr_remove_chr(box->text, al_ustr_offset(box->text, box->cursor_pos)); } break; } case ALLEGRO_KEY_LEFT: { if (box->cursor_pos > 0) { box->cursor_pos--; } else ret = 0; break; } case ALLEGRO_KEY_RIGHT: { if (box->cursor_pos < len) { box->cursor_pos++; } else ret = 0; break; } case ALLEGRO_KEY_HOME: { box->cursor_pos = 0; break; } case ALLEGRO_KEY_END: { len = al_ustr_length(box->text); box->cursor_pos = len; break; } case ALLEGRO_KEY_ENTER: { wz_trigger(wgt); break; } default: ret = 0; } } wz_snap_editbox(box); break; } case WZ_SET_CURSOR_POS: { box->cursor_pos = event->user.data3; wz_snap_editbox(box); } case WZ_SET_TEXT: { if(box->own) { al_ustr_assign(box->text, (ALLEGRO_USTR*)event->user.data3); } else box->text = (ALLEGRO_USTR*)event->user.data3; wz_snap_editbox(box); break; } case WZ_TRIGGER: { ALLEGRO_EVENT ev; wz_craft_event(&ev, WZ_TEXT_CHANGED, wgt, 0); al_emit_user_event(wgt->source, &ev, 0); break; } case ALLEGRO_EVENT_MOUSE_AXES: { if (wgt->flags & WZ_STATE_DISABLED) { ret = 0; } if (wz_widget_rect_test(wgt, event->mouse.x, event->mouse.y)) { wz_ask_parent_for_focus(wgt); } return wz_widget_proc(wgt, event); break; } default: ret = 0; } if (ret == 0) ret = wz_widget_proc(wgt, event); return ret; }
/* Title: Widget Section: Internal Function: wz_widget_proc Callback function that handles the operations of a general widget. All widget procs call this function as their default handler. Returns: 1 if the event was handled by the widget, 0 otherwise */ int wz_widget_proc(WZ_WIDGET* wgt, ALLEGRO_EVENT* event) { int ret = 1; switch (event->type) { case WZ_HIDE: { wgt->flags |= WZ_STATE_HIDDEN; break; } case WZ_SHOW: { wgt->flags &= ~WZ_STATE_HIDDEN; break; } case WZ_DISABLE: { wgt->flags |= WZ_STATE_DISABLED; break; } case WZ_ENABLE: { wgt->flags &= ~WZ_STATE_DISABLED; break; } case WZ_UPDATE_POSITION: { if (wgt->parent) { wgt->local_x = wgt->parent->local_x + wgt->x; wgt->local_y = wgt->parent->local_y + wgt->y; } else { wgt->local_x = wgt->x; wgt->local_y = wgt->y; } break; } case WZ_DESTROY: { al_destroy_user_event_source(wgt->source); free(wgt->source); free(wgt); break; } case WZ_LOSE_FOCUS: { wgt->flags &= ~WZ_STATE_HAS_FOCUS; break; } case WZ_TAKE_FOCUS: { wz_focus(wgt, 0); wgt->flags |= WZ_STATE_HAS_FOCUS; if (wgt->first_child) wz_focus(wgt->first_child, 1); break; } case WZ_WANT_FOCUS: { WZ_WIDGET* child = wgt->first_child; while (child) { wz_focus(child, 0); child = child->next_sib; } { ALLEGRO_EVENT ev; wz_craft_event(&ev, WZ_TAKE_FOCUS, wgt, 0); child = wgt->first_child; wz_send_event((WZ_WIDGET*)event->user.data2, &ev); } break; } case ALLEGRO_EVENT_MOUSE_AXES: { if (wgt->flags & WZ_STATE_DISABLED) { ret = 0; } else if (event->mouse.dz != 0 && wgt->first_child == 0 && wgt->parent != 0) { if (event->mouse.dz > 0) { wz_ask_parent_to_focus_prev(wgt); } else { wz_ask_parent_to_focus_next(wgt); } } else { ret = 0; } break; } /* Switch through elements on Touch: case ALLEGRO_EVENT_TOUCH_BEGIN: { if (wgt->flags & WZ_STATE_DISABLED) { ret = 0; } else if (wgt->first_child == 0 && wgt->parent != 0) { wz_ask_parent_to_focus_next(wgt); } else { ret = 0; } break; } */ case ALLEGRO_EVENT_KEY_CHAR: { if(event->keyboard.keycode == wgt->shortcut.keycode && ((event->keyboard.modifiers & wgt->shortcut.modifiers) || wgt->shortcut.modifiers == 0)) { ALLEGRO_EVENT ev; wz_craft_event(&ev, WZ_HANDLE_SHORTCUT, wgt, 0); wz_send_event(wgt, &ev); } else { switch (event->keyboard.keycode) { case ALLEGRO_KEY_TAB: { if (wgt->first_child != 0) { ret = 0; } else if (event->keyboard.modifiers & 1) { wz_ask_parent_to_focus_prev(wgt); } else { wz_ask_parent_to_focus_next(wgt); } break; } case ALLEGRO_KEY_UP: { if (wgt->first_child != 0) { ret = 0; } else if (wgt->parent != 0) { wz_ask_parent_for_focus(wz_get_widget_dir(wgt, 0)); } else ret = 0; break; } case ALLEGRO_KEY_RIGHT: { if (wgt->first_child != 0) { ret = 0; } else if (wgt->parent != 0) { wz_ask_parent_for_focus(wz_get_widget_dir(wgt, 1)); } else ret = 0; break; } case ALLEGRO_KEY_DOWN: { if (wgt->first_child != 0) { ret = 0; } else if (wgt->parent != 0) { wz_ask_parent_for_focus(wz_get_widget_dir(wgt, 2)); } else ret = 0; break; } case ALLEGRO_KEY_LEFT: { if (wgt->first_child != 0) { ret = 0; } else if (wgt->parent != 0) { wz_ask_parent_for_focus(wz_get_widget_dir(wgt, 3)); } else ret = 0; break; } default: ret = 0; } } break; } default: ret = 0; } return ret; }