mirror of
https://github.com/FIX94/hbc.git
synced 2024-12-01 15:04:20 +01:00
405 lines
7.7 KiB
C
405 lines
7.7 KiB
C
#include <stdio.h>
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
|
|
#include <ogcsys.h>
|
|
|
|
#include "../config.h"
|
|
#include "controls.h"
|
|
#include "theme.h"
|
|
#include "cursors.h"
|
|
#include "widgets.h"
|
|
#include "bubbles.h"
|
|
#include "panic.h"
|
|
#include "view.h"
|
|
|
|
#define FOCUS_FLAGS (WF_VISIBLE | WF_ENABLED | WF_FOCUSABLE)
|
|
#define CURSOR_FLAGS (WF_VISIBLE | WF_ENABLED)
|
|
|
|
#define RUMBLE_TIME 3
|
|
#define RUMBLE_DELAY 12
|
|
|
|
u8 *cursor_data;
|
|
u32 cursor_data_size;
|
|
u8 *cursor_shade_data;
|
|
u32 cursor_shade_data_size;
|
|
|
|
static gfx_queue_entry entry_bg;
|
|
static gfx_queue_entry entry_logo;
|
|
static gfx_queue_entry entry_throbber;
|
|
|
|
static gfx_entity gradient_subview;
|
|
static gfx_queue_entry entry_subview;
|
|
|
|
static bool cursor_enabled;
|
|
static bool rumble_enabled;
|
|
static bool throbber_enabled;
|
|
|
|
static gfx_queue_entry cur[2];
|
|
|
|
static s8 rumble_timeout = -RUMBLE_DELAY;
|
|
|
|
bool view_bubbles = false;
|
|
|
|
void view_init (void) {
|
|
cursor_enabled = false;
|
|
rumble_enabled = (CONF_GetPadMotorMode() == 0) ? 0 : 1;
|
|
throbber_enabled = false;
|
|
|
|
view_theme_reinit();
|
|
bubbles_init();
|
|
}
|
|
|
|
void view_deinit (void) {
|
|
cursor_enabled = false;
|
|
bubbles_deinit();
|
|
}
|
|
|
|
void view_theme_reinit(void) {
|
|
gfx_qe_entity(&entry_bg, theme_gfx[THEME_BACKGROUND],
|
|
0, 0, -3, COL_DEFAULT);
|
|
gfx_qe_entity(&entry_logo, theme_gfx[THEME_LOGO], 0, 416, 0, COL_DEFAULT);
|
|
gfx_qe_entity(&entry_throbber, theme_gfx[THEME_THROBBER],
|
|
(view_width - theme_gfx[THEME_THROBBER]->w) / 2,
|
|
(view_height - theme_gfx[THEME_THROBBER]->h) / 2,
|
|
0, COL_DEFAULT);
|
|
}
|
|
|
|
view * view_new (u8 widget_count, const view *sub_view, s16 x, s16 y, s16 z,
|
|
u32 drag_btn) {
|
|
view *v;
|
|
|
|
v = (view *) pmalloc (sizeof (view));
|
|
memset (v, 0, sizeof (view));
|
|
|
|
v->coords.x = x;
|
|
v->coords.y = y;
|
|
v->coords.z = z;
|
|
gfx_qe_origin_push (&v->qe_coords_push, &v->coords);
|
|
gfx_qe_origin_pop (&v->qe_coords_pop);
|
|
|
|
v->widget_count = widget_count;
|
|
|
|
v->widgets = (widget *) pmalloc (widget_count * sizeof (widget));
|
|
memset (v->widgets, 0, widget_count * sizeof (widget));
|
|
|
|
v->sub_view = sub_view;
|
|
|
|
v->focus = -1;
|
|
v->cursor = -1;
|
|
|
|
v->drag = false;
|
|
v->drag_btn = drag_btn;
|
|
|
|
return v;
|
|
}
|
|
|
|
void view_free (view *v) {
|
|
u8 i;
|
|
|
|
if (!v)
|
|
return;
|
|
|
|
for (i = 0; i < v->widget_count; ++i)
|
|
widget_free(&v->widgets[i]);
|
|
|
|
free(v->widgets);
|
|
free(v);
|
|
}
|
|
|
|
static void view_push_view (const view *v, u32 subview_alpha) {
|
|
int i, j;
|
|
widget *w;
|
|
widget_layer *l;
|
|
u32 f;
|
|
|
|
if (v->sub_view) {
|
|
view_push_view (v->sub_view, false);
|
|
gfx_gen_gradient (&gradient_subview, view_width + 10,
|
|
view_height + 10, subview_alpha,
|
|
subview_alpha, subview_alpha, subview_alpha);
|
|
|
|
gfx_qe_entity (&entry_subview, &gradient_subview, -5, -5,
|
|
v->coords.z - 1, COL_DEFAULT);
|
|
|
|
gfx_frame_push (&entry_subview, 1);
|
|
}
|
|
|
|
gfx_frame_push (&v->qe_coords_push, 1);
|
|
|
|
for (i = 0; i < v->widget_count; ++i) {
|
|
w = &v->widgets[i];
|
|
|
|
if (w->flags & WF_VISIBLE) {
|
|
gfx_frame_push (&w->qe_coords_push, 1);
|
|
for (j = 0; j < w->layer_count; ++j) {
|
|
l = &w->layers[j];
|
|
|
|
if (l->flags) {
|
|
f = w->flags ^ l->flags_invert;
|
|
f = f & l->flags & WF_FLAGS_MASK;
|
|
|
|
if (!f)
|
|
continue;
|
|
|
|
if (l->flags & WF_FLAGS_AND &&
|
|
f != (l->flags & WF_FLAGS_MASK))
|
|
continue;
|
|
}
|
|
|
|
gfx_frame_push (w->layers[j].gfx_entries,
|
|
w->layers[j].gfx_entry_count);
|
|
}
|
|
|
|
gfx_frame_push (&w->qe_coords_pop, 1);
|
|
}
|
|
}
|
|
|
|
gfx_frame_push (&v->qe_coords_pop, 1);
|
|
}
|
|
|
|
void view_plot (view *v, u32 alpha, u32 *down, u32 *held, u32 *up) {
|
|
u32 bd, bh, bu;
|
|
bool wm;
|
|
s32 x, y;
|
|
f32 roll;
|
|
s8 w, c;
|
|
cursor_type ct;
|
|
#ifdef ENABLE_SCREENSHOTS
|
|
u16 ss_x, ss_y;
|
|
u32 *ss_buf = NULL;
|
|
#endif
|
|
|
|
controls_scan (&bd, &bh, &bu);
|
|
wm = controls_ir (&x, &y, &roll);
|
|
w = view_widget_at_xy (v, x - v->coords.x, y - v->coords.y);
|
|
ct = CUR_STD;
|
|
|
|
if (v->drag_btn) {
|
|
if (bu & v->drag_btn)
|
|
v->drag = false;
|
|
|
|
if ((w != -1) && (bd & v->drag_btn)) {
|
|
v->drag = true;
|
|
|
|
v->drag_widget = w;
|
|
|
|
v->drag_start_x = x;
|
|
v->drag_start_y = y;
|
|
}
|
|
|
|
if (v->drag && (bh & v->drag_btn)) {
|
|
v->drag_x = x - v->drag_start_x;
|
|
v->drag_y = y - v->drag_start_y;
|
|
|
|
ct = v->widgets[v->drag_widget].cur;
|
|
}
|
|
}
|
|
|
|
c = v->cursor;
|
|
|
|
if (v->cursor != -1) {
|
|
widget_set_flag (&v->widgets[v->cursor], WF_CURSOR, false);
|
|
v->cursor = -1;
|
|
}
|
|
|
|
wm = wm && cursor_enabled;
|
|
|
|
if (wm) {
|
|
view_set_focus (v, -1);
|
|
|
|
if (w != -1) {
|
|
if ((v->widgets[w].flags & CURSOR_FLAGS) == CURSOR_FLAGS) {
|
|
widget_set_flag (&v->widgets[w], WF_CURSOR, true);
|
|
v->cursor = w;
|
|
|
|
if (rumble_enabled && (c != w) && (ct != CUR_DRAG) &&
|
|
(v->widgets[w].flags & WF_RUMBLE) &&
|
|
(rumble_timeout == -RUMBLE_DELAY)) {
|
|
rumble_timeout = RUMBLE_TIME;
|
|
controls_rumble(1);
|
|
}
|
|
}
|
|
|
|
if ((v->widgets[w].flags & FOCUS_FLAGS) == FOCUS_FLAGS)
|
|
view_set_focus (v, w);
|
|
}
|
|
}
|
|
|
|
gfx_frame_start ();
|
|
|
|
gfx_frame_push (&entry_bg, 1);
|
|
|
|
if (view_bubbles)
|
|
bubble_update(wm, x, y);
|
|
|
|
gfx_frame_push (&entry_logo, 1);
|
|
|
|
view_push_view (v, alpha);
|
|
|
|
if (throbber_enabled)
|
|
gfx_frame_push(&entry_throbber, 1);
|
|
|
|
if (wm) {
|
|
if ((w != -1) && (ct != CUR_DRAG))
|
|
ct = v->widgets[w].cur;
|
|
|
|
cursors_queue (cur, ct, x, y, roll);
|
|
|
|
gfx_frame_push (cur, 2);
|
|
}
|
|
|
|
#ifdef ENABLE_SCREENSHOTS
|
|
// ZR + ZL on Classic Controller
|
|
if ((bh & PADS_NET_INIT) && (bd & PADS_SCREENSHOT)) {
|
|
gfx_get_efb_size(&ss_x, &ss_y);
|
|
ss_buf = (u32 *) pmalloc(ss_x * ss_y * sizeof(u32));
|
|
|
|
gfx_set_efb_buffer(ss_buf);
|
|
}
|
|
#endif
|
|
|
|
gfx_frame_end ();
|
|
|
|
#ifdef ENABLE_SCREENSHOTS
|
|
if (ss_buf) {
|
|
save_rgba_png(ss_buf, ss_x, ss_y);
|
|
free(ss_buf);
|
|
}
|
|
#endif
|
|
|
|
if (down)
|
|
*down = bd;
|
|
|
|
if (held)
|
|
*held = bh;
|
|
|
|
if (up)
|
|
*up = bu;
|
|
|
|
if (rumble_timeout > -RUMBLE_DELAY)
|
|
rumble_timeout--;
|
|
|
|
if (rumble_timeout < 1)
|
|
controls_rumble(0);
|
|
}
|
|
|
|
void view_fade (view *v, s16 z, u32 c1, u32 c2, u32 c3, u32 c4, u8 steps,
|
|
s8 modifier) {
|
|
view *vf;
|
|
u8 i;
|
|
|
|
vf = view_new (1, v, 0, 0, 0, 0);
|
|
|
|
widget_gradient (&vf->widgets[0], -32, -32, z, view_width + 64,
|
|
view_height + 64, c1, c2, c3, c4);
|
|
|
|
for (i = 0; i < steps; ++i) {
|
|
widget_fade_gradient (&vf->widgets[0], modifier);
|
|
|
|
view_plot (vf, 0, NULL, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
void view_set_focus (view *v, s8 new_focus) {
|
|
if (v->focus != -1)
|
|
v->widgets[v->focus].flags &= ~((u32) WF_FOCUSED);
|
|
|
|
v->focus = new_focus;
|
|
|
|
if (v->focus != -1)
|
|
v->widgets[v->focus].flags |= WF_FOCUSED;
|
|
}
|
|
|
|
u8 view_set_focus_prev (view *v) {
|
|
s16 i;
|
|
|
|
if (v->focus < 1)
|
|
return v->focus;
|
|
|
|
for (i = v->focus - 1; i >= 0; --i)
|
|
if ((v->widgets[i].flags & FOCUS_FLAGS) == FOCUS_FLAGS) {
|
|
view_set_focus (v, i);
|
|
|
|
break;
|
|
}
|
|
|
|
return v->focus;
|
|
}
|
|
|
|
u8 view_set_focus_next (view *v) {
|
|
u8 i;
|
|
|
|
if (v->focus == v->widget_count - 1)
|
|
return v->focus;
|
|
|
|
for (i = v->focus + 1; i < v->widget_count; ++i)
|
|
if ((v->widgets[i].flags & FOCUS_FLAGS) == FOCUS_FLAGS) {
|
|
view_set_focus (v, i);
|
|
|
|
break;
|
|
}
|
|
|
|
return v->focus;
|
|
}
|
|
|
|
u8 view_move_focus(view *v, s8 mod) {
|
|
if (v->focus + mod < 1)
|
|
return v->focus;
|
|
|
|
if (v->focus + mod > v->widget_count - 1)
|
|
return v->focus;
|
|
|
|
if ((v->widgets[v->focus + mod].flags & FOCUS_FLAGS) == FOCUS_FLAGS)
|
|
view_set_focus (v, v->focus + mod);
|
|
|
|
return v->focus;
|
|
}
|
|
|
|
void view_enable_cursor (bool enable) {
|
|
cursor_enabled = enable;
|
|
}
|
|
|
|
s8 view_widget_at_xy (const view *v, s32 x, s32 y) {
|
|
s16 i;
|
|
widget *w;
|
|
s16 wx, wy;
|
|
|
|
for (i = v->widget_count - 1; i >= 0; --i) {
|
|
w = &v->widgets[i];
|
|
|
|
if (w->type == WT_MEMO) {
|
|
wx = w->coords.x;
|
|
wy = w->memo.y_max;
|
|
} else {
|
|
wx = w->coords.x;
|
|
wy = w->coords.y;
|
|
}
|
|
|
|
if (w->width && w->height && (x >= wx) && (y >= wy) &&
|
|
(x <= wx + w->width) && (y <= wy + w->height))
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
s8 view_widget_at_ir (const view *v) {
|
|
s32 x, y;
|
|
|
|
if (!cursor_enabled || !controls_ir (&x, &y, NULL))
|
|
return -1;
|
|
|
|
return view_widget_at_xy (v, x - v->coords.x, y - v->coords.y);
|
|
}
|
|
|
|
void view_show_throbber(bool show) {
|
|
entry_throbber.entity.rad = 0;
|
|
throbber_enabled = show;
|
|
}
|
|
|
|
void view_throbber_tickle(void) {
|
|
entry_throbber.entity.rad -= 0.1f;
|
|
}
|
|
|