mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
PartsEngine: Implement LayoutBox
This adds PARTS_LAYOUT_BOX parts type with support for automatic horizontal/vertical child positioning. Layout calculation runs during the component update cycle, repositioning children before their global parameters are combined.
This commit is contained in:
@@ -100,6 +100,7 @@ target_sources(xsystem4 PRIVATE
|
||||
src/parts/flash.c
|
||||
src/parts/flat.c
|
||||
src/parts/input.c
|
||||
src/parts/layoutbox.c
|
||||
src/parts/motion.c
|
||||
src/parts/parts.c
|
||||
src/parts/render.c
|
||||
|
||||
@@ -285,4 +285,23 @@ bool PE_StartPartsFlat(int parts_no, int state);
|
||||
bool PE_GoFramePartsFlat(int parts_no, int frame_no, int state);
|
||||
int PE_GetPartsFlatEndFrame(int parts_no, int state);
|
||||
|
||||
// layoutbox.c
|
||||
void PE_SetLayoutBoxLayoutType(int parts_no, int type);
|
||||
int PE_GetLayoutBoxLayoutType(int parts_no);
|
||||
void PE_SetLayoutBoxReturn(int parts_no, bool return_flag, int return_size);
|
||||
bool PE_IsLayoutBoxReturn(int parts_no);
|
||||
int PE_GetLayoutBoxReturnSize(int parts_no);
|
||||
void PE_SetLayoutBoxAlign(int parts_no, int align);
|
||||
int PE_GetLayoutBoxAlign(int parts_no);
|
||||
void PE_SetComponentMargin(int parts_no, int top, int bottom, int left, int right);
|
||||
int PE_GetComponentMarginTop(int parts_no);
|
||||
int PE_GetComponentMarginBottom(int parts_no);
|
||||
int PE_GetComponentMarginLeft(int parts_no);
|
||||
int PE_GetComponentMarginRight(int parts_no);
|
||||
void PE_set_layoutbox_padding(int parts_no, int top, int bottom, int left, int right);
|
||||
int PE_get_layoutbox_padding_top(int parts_no);
|
||||
int PE_get_layoutbox_padding_bottom(int parts_no);
|
||||
int PE_get_layoutbox_padding_left(int parts_no);
|
||||
int PE_get_layoutbox_padding_right(int parts_no);
|
||||
|
||||
#endif /* SYSTEM4_PARTS_H */
|
||||
|
||||
+36
-12
@@ -282,6 +282,30 @@ static int PartsEngine_PartsFunc(int func_id, struct page **array_int,
|
||||
REQUIRE_INTS(2);
|
||||
PE_parts_set_lock_input_state(ints[0].i, !!ints[1].i);
|
||||
return 1;
|
||||
case 57: // void AppendChild(int number, int child_number)
|
||||
REQUIRE_INTS(2);
|
||||
PE_SetParentPartsNumber(ints[1].i, ints[0].i);
|
||||
return 1;
|
||||
case 91: // void SetLayoutBoxPadding(int parts_no, int top, int bottom, int left, int right)
|
||||
REQUIRE_INTS(5);
|
||||
PE_set_layoutbox_padding(ints[0].i, ints[1].i, ints[2].i, ints[3].i, ints[4].i);
|
||||
return 1;
|
||||
case 92: // int GetLayoutBoxPaddingTop(int parts_no)
|
||||
REQUIRE_INTS(2);
|
||||
ints[1].i = PE_get_layoutbox_padding_top(ints[0].i);
|
||||
return 1;
|
||||
case 93: // int GetLayoutBoxPaddingBottom(int parts_no)
|
||||
REQUIRE_INTS(2);
|
||||
ints[1].i = PE_get_layoutbox_padding_bottom(ints[0].i);
|
||||
return 1;
|
||||
case 94: // int GetLayoutBoxPaddingLeft(int parts_no)
|
||||
REQUIRE_INTS(2);
|
||||
ints[1].i = PE_get_layoutbox_padding_left(ints[0].i);
|
||||
return 1;
|
||||
case 95: // int GetLayoutBoxPaddingRight(int parts_no)
|
||||
REQUIRE_INTS(2);
|
||||
ints[1].i = PE_get_layoutbox_padding_right(ints[0].i);
|
||||
return 1;
|
||||
case 103: // void GetPartsCGSurfaceArea(int parts_no, int *x, int *y, int *w, int *h, int state)
|
||||
REQUIRE_INTS(6);
|
||||
PE_GetPartsCGSurfaceArea(ints[0].i, &ints[1].i, &ints[2].i, &ints[3].i, &ints[4].i, ints[5].i);
|
||||
@@ -552,11 +576,11 @@ HLL_LIBRARY(PartsEngine,
|
||||
HLL_TODO_EXPORT(GetComponentRotateX, PartsEngine_GetComponentRotateX),
|
||||
HLL_TODO_EXPORT(GetComponentRotateY, PartsEngine_GetComponentRotateY),
|
||||
HLL_EXPORT(GetComponentRotateZ, PE_GetPartsRotateZ),
|
||||
HLL_TODO_EXPORT(SetComponentMargin, PartsEngine_SetComponentMargin),
|
||||
HLL_TODO_EXPORT(GetComponentMarginTop, PartsEngine_GetComponentMarginTop),
|
||||
HLL_TODO_EXPORT(GetComponentMarginBottom, PartsEngine_GetComponentMarginBottom),
|
||||
HLL_TODO_EXPORT(GetComponentMarginLeft, PartsEngine_GetComponentMarginLeft),
|
||||
HLL_TODO_EXPORT(GetComponentMarginRight, PartsEngine_GetComponentMarginRight),
|
||||
HLL_EXPORT(SetComponentMargin, PE_SetComponentMargin),
|
||||
HLL_EXPORT(GetComponentMarginTop, PE_GetComponentMarginTop),
|
||||
HLL_EXPORT(GetComponentMarginBottom, PE_GetComponentMarginBottom),
|
||||
HLL_EXPORT(GetComponentMarginLeft, PE_GetComponentMarginLeft),
|
||||
HLL_EXPORT(GetComponentMarginRight, PE_GetComponentMarginRight),
|
||||
HLL_EXPORT(SetComponentAlphaClipper, PE_SetPartsAlphaClipperPartsNumber),
|
||||
HLL_TODO_EXPORT(GetComponentAlphaClipper, PartsEngine_GetComponentAlphaClipper),
|
||||
HLL_TODO_EXPORT(SetComponentTextureFilterType, PartsEngine_SetComponentTextureFilterType),
|
||||
@@ -724,13 +748,13 @@ HLL_LIBRARY(PartsEngine,
|
||||
HLL_TODO_EXPORT(GetMultiTextBoxSelectB, PartsEngine_GetMultiTextBoxSelectB),
|
||||
HLL_TODO_EXPORT(SetMultiTextBoxCGName, PartsEngine_SetMultiTextBoxCGName),
|
||||
HLL_TODO_EXPORT(GetMultiTextBoxCGName, PartsEngine_GetMultiTextBoxCGName),
|
||||
HLL_TODO_EXPORT(SetLayoutBoxLayoutType, PartsEngine_SetLayoutBoxLayoutType),
|
||||
HLL_TODO_EXPORT(GetLayoutBoxLayoutType, PartsEngine_GetLayoutBoxLayoutType),
|
||||
HLL_TODO_EXPORT(SetLayoutBoxReturn, PartsEngine_SetLayoutBoxReturn),
|
||||
HLL_TODO_EXPORT(IsLayoutBoxReturn, PartsEngine_IsLayoutBoxReturn),
|
||||
HLL_TODO_EXPORT(GetLayoutBoxReturnSize, PartsEngine_GetLayoutBoxReturnSize),
|
||||
HLL_TODO_EXPORT(SetLayoutBoxAlign, PartsEngine_SetLayoutBoxAlign),
|
||||
HLL_TODO_EXPORT(GetLayoutBoxAlign, PartsEngine_GetLayoutBoxAlign),
|
||||
HLL_EXPORT(SetLayoutBoxLayoutType, PE_SetLayoutBoxLayoutType),
|
||||
HLL_EXPORT(GetLayoutBoxLayoutType, PE_GetLayoutBoxLayoutType),
|
||||
HLL_EXPORT(SetLayoutBoxReturn, PE_SetLayoutBoxReturn),
|
||||
HLL_EXPORT(IsLayoutBoxReturn, PE_IsLayoutBoxReturn),
|
||||
HLL_EXPORT(GetLayoutBoxReturnSize, PE_GetLayoutBoxReturnSize),
|
||||
HLL_EXPORT(SetLayoutBoxAlign, PE_SetLayoutBoxAlign),
|
||||
HLL_EXPORT(GetLayoutBoxAlign, PE_GetLayoutBoxAlign),
|
||||
HLL_EXPORT(Parts_SetPartsCG, PE_SetPartsCG),
|
||||
HLL_EXPORT(Parts_GetPartsCGName, PE_GetPartsCGName),
|
||||
HLL_EXPORT(Parts_SetPartsCGSurfaceArea, PE_SetPartsCGSurfaceArea),
|
||||
|
||||
@@ -61,6 +61,7 @@ xsystem4 = [version_h,
|
||||
'parts/flash.c',
|
||||
'parts/flat.c',
|
||||
'parts/input.c',
|
||||
'parts/layoutbox.c',
|
||||
'parts/message.c',
|
||||
'parts/motion.c',
|
||||
'parts/parts.c',
|
||||
|
||||
@@ -272,6 +272,10 @@ static cJSON *parts_state_to_json(struct parts_state *state, bool verbose)
|
||||
break;
|
||||
case PARTS_RECT_DETECTION:
|
||||
break;
|
||||
case PARTS_LAYOUT_BOX:
|
||||
cJSON_AddNumberToObject(obj, "layout_type", state->layout_box.layout_type);
|
||||
cJSON_AddNumberToObject(obj, "align", state->layout_box.align);
|
||||
break;
|
||||
}
|
||||
|
||||
return obj;
|
||||
@@ -566,6 +570,9 @@ static void parts_list_print(struct parts *parts, int indent)
|
||||
case PARTS_RECT_DETECTION:
|
||||
sys_message("(rect_detection)");
|
||||
break;
|
||||
case PARTS_LAYOUT_BOX:
|
||||
sys_message("(layout_box type=%d)", state->layout_box.layout_type);
|
||||
break;
|
||||
}
|
||||
|
||||
sys_message(" @ controller=%d z=%d (%d)\n", parts->controller_no, parts->global.z, parts->local.z);
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
/* Copyright (C) 2026 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "system4/flat.h"
|
||||
|
||||
#include "parts.h"
|
||||
#include "parts_internal.h"
|
||||
|
||||
void PE_SetComponentMargin(int parts_no, int top, int bottom, int left, int right)
|
||||
{
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
parts->margin_top = top;
|
||||
parts->margin_bottom = bottom;
|
||||
parts->margin_left = left;
|
||||
parts->margin_right = right;
|
||||
}
|
||||
|
||||
int PE_GetComponentMarginTop(int parts_no)
|
||||
{
|
||||
return parts_get(parts_no)->margin_top;
|
||||
}
|
||||
|
||||
int PE_GetComponentMarginBottom(int parts_no)
|
||||
{
|
||||
return parts_get(parts_no)->margin_bottom;
|
||||
}
|
||||
|
||||
int PE_GetComponentMarginLeft(int parts_no)
|
||||
{
|
||||
return parts_get(parts_no)->margin_left;
|
||||
}
|
||||
|
||||
int PE_GetComponentMarginRight(int parts_no)
|
||||
{
|
||||
return parts_get(parts_no)->margin_right;
|
||||
}
|
||||
|
||||
void PE_SetLayoutBoxLayoutType(int parts_no, int type)
|
||||
{
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_layout_box *lb = parts_get_layout_box(parts);
|
||||
if (lb->layout_type != type) {
|
||||
lb->layout_type = type;
|
||||
parts_component_dirty(parts);
|
||||
}
|
||||
}
|
||||
|
||||
int PE_GetLayoutBoxLayoutType(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return PARTS_LAYOUT_FREE;
|
||||
return parts->states[0].layout_box.layout_type;
|
||||
}
|
||||
|
||||
void PE_SetLayoutBoxReturn(int parts_no, bool return_flag, int return_size)
|
||||
{
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_layout_box *lb = parts_get_layout_box(parts);
|
||||
lb->wrap = return_flag;
|
||||
lb->wrap_size = return_size;
|
||||
parts_component_dirty(parts);
|
||||
}
|
||||
|
||||
bool PE_IsLayoutBoxReturn(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return false;
|
||||
return parts->states[0].layout_box.wrap;
|
||||
}
|
||||
|
||||
int PE_GetLayoutBoxReturnSize(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.wrap_size;
|
||||
}
|
||||
|
||||
void PE_SetLayoutBoxAlign(int parts_no, int align)
|
||||
{
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_layout_box *lb = parts_get_layout_box(parts);
|
||||
if (lb->align != align) {
|
||||
lb->align = align;
|
||||
parts_component_dirty(parts);
|
||||
}
|
||||
}
|
||||
|
||||
int PE_GetLayoutBoxAlign(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.align;
|
||||
}
|
||||
|
||||
void PE_set_layoutbox_padding(int parts_no, int top, int bottom, int left, int right)
|
||||
{
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
struct parts_layout_box *lb = parts_get_layout_box(parts);
|
||||
lb->padding_top = top;
|
||||
lb->padding_bottom = bottom;
|
||||
lb->padding_left = left;
|
||||
lb->padding_right = right;
|
||||
parts_component_dirty(parts);
|
||||
}
|
||||
|
||||
int PE_get_layoutbox_padding_top(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.padding_top;
|
||||
}
|
||||
|
||||
int PE_get_layoutbox_padding_bottom(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.padding_bottom;
|
||||
}
|
||||
|
||||
int PE_get_layoutbox_padding_left(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.padding_left;
|
||||
}
|
||||
|
||||
int PE_get_layoutbox_padding_right(int parts_no)
|
||||
{
|
||||
struct parts *parts = parts_try_get(parts_no);
|
||||
if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return 0;
|
||||
return parts->states[0].layout_box.padding_right;
|
||||
}
|
||||
|
||||
static void parts_get_layout_size(struct parts *parts, int *w, int *h)
|
||||
{
|
||||
struct parts_state *state = &parts->states[parts->state];
|
||||
if (state->type == PARTS_FLAT && state->flat.flat) {
|
||||
// The layout size of Flat is specified in the header, and this may
|
||||
// differ from the hitbox size.
|
||||
*w = state->flat.flat->hdr.width;
|
||||
*h = state->flat.flat->hdr.height;
|
||||
} else {
|
||||
*w = state->common.w;
|
||||
*h = state->common.h;
|
||||
}
|
||||
}
|
||||
|
||||
static int align_offset_x(int align, int total_w)
|
||||
{
|
||||
switch (align) {
|
||||
case 2: case 5: case 8: return -(total_w / 2);
|
||||
case 3: case 6: case 9: return -total_w;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int align_offset_y(int align, int total_h)
|
||||
{
|
||||
switch (align) {
|
||||
case 4: case 5: case 6: return -(total_h / 2);
|
||||
case 7: case 8: case 9: return -total_h;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates the total size of a horizontal layout box by summing child widths per row.
|
||||
static void parts_layout_calc_size_horizontal(struct parts_layout_box *lb, struct parts *parts,
|
||||
int *out_w, int *out_h)
|
||||
{
|
||||
int row_w = 0, max_row_w = 0, max_row_h = 0, total_h = 0;
|
||||
struct parts *child;
|
||||
PARTS_FOREACH_CHILD(child, parts) {
|
||||
int child_w, child_h;
|
||||
parts_get_layout_size(child, &child_w, &child_h);
|
||||
int cw = child->margin_left + child_w + child->margin_right;
|
||||
int ch = child->margin_top + child_h + child->margin_bottom;
|
||||
if (lb->wrap && row_w + cw > lb->wrap_size && row_w > 0) {
|
||||
if (row_w > max_row_w)
|
||||
max_row_w = row_w;
|
||||
total_h += max_row_h;
|
||||
row_w = 0;
|
||||
max_row_h = 0;
|
||||
}
|
||||
row_w += cw;
|
||||
if (ch > max_row_h)
|
||||
max_row_h = ch;
|
||||
}
|
||||
if (row_w > max_row_w)
|
||||
max_row_w = row_w;
|
||||
total_h += max_row_h;
|
||||
*out_w = max_row_w + lb->padding_left + lb->padding_right;
|
||||
*out_h = total_h + lb->padding_top + lb->padding_bottom;
|
||||
}
|
||||
|
||||
// Calculates the total size of a vertical layout box by summing child heights per column.
|
||||
static void parts_layout_calc_size_vertical(struct parts_layout_box *lb, struct parts *parts,
|
||||
int *out_w, int *out_h)
|
||||
{
|
||||
int col_h = 0, max_col_h = 0, max_col_w = 0, total_w = 0;
|
||||
struct parts *child;
|
||||
PARTS_FOREACH_CHILD(child, parts) {
|
||||
int child_w, child_h;
|
||||
parts_get_layout_size(child, &child_w, &child_h);
|
||||
int cw = child->margin_left + child_w + child->margin_right;
|
||||
int ch = child->margin_top + child_h + child->margin_bottom;
|
||||
if (lb->wrap && col_h + ch > lb->wrap_size && col_h > 0) {
|
||||
if (col_h > max_col_h)
|
||||
max_col_h = col_h;
|
||||
total_w += max_col_w;
|
||||
col_h = 0;
|
||||
max_col_w = 0;
|
||||
}
|
||||
col_h += ch;
|
||||
if (cw > max_col_w)
|
||||
max_col_w = cw;
|
||||
}
|
||||
if (col_h > max_col_h)
|
||||
max_col_h = col_h;
|
||||
total_w += max_col_w;
|
||||
*out_w = total_w + lb->padding_left + lb->padding_right;
|
||||
*out_h = max_col_h + lb->padding_top + lb->padding_bottom;
|
||||
}
|
||||
|
||||
void parts_do_layout(struct parts *parts)
|
||||
{
|
||||
if (parts->states[0].type != PARTS_LAYOUT_BOX)
|
||||
return;
|
||||
struct parts_layout_box *lb = &parts->states[0].layout_box;
|
||||
if (lb->layout_type == PARTS_LAYOUT_FREE)
|
||||
return;
|
||||
|
||||
int total_w, total_h;
|
||||
if (lb->layout_type == PARTS_LAYOUT_HORIZONTAL)
|
||||
parts_layout_calc_size_horizontal(lb, parts, &total_w, &total_h);
|
||||
else
|
||||
parts_layout_calc_size_vertical(lb, parts, &total_w, &total_h);
|
||||
|
||||
int align = lb->align;
|
||||
bool is_left = (align == 1 || align == 4 || align == 7);
|
||||
bool is_right = (align == 3 || align == 6 || align == 9);
|
||||
bool is_top = (align == 1 || align == 2 || align == 3);
|
||||
bool is_bottom = (align == 7 || align == 8 || align == 9);
|
||||
|
||||
int origin_x = align_offset_x(parts->origin_mode, total_w);
|
||||
int origin_y = align_offset_y(parts->origin_mode, total_h);
|
||||
int align_x = align_offset_x(align, total_w);
|
||||
int align_y = align_offset_y(align, total_h);
|
||||
|
||||
int start_x = (is_left ? lb->padding_left : -lb->padding_right)
|
||||
+ (origin_x - align_x);
|
||||
int start_y = (is_top ? lb->padding_top : -lb->padding_bottom)
|
||||
+ (origin_y - align_y);
|
||||
|
||||
int x_dir = is_right ? -1 : 1;
|
||||
int y_dir = is_bottom ? -1 : 1;
|
||||
|
||||
int cur_x = start_x;
|
||||
int cur_y = start_y;
|
||||
int max_cross = 0;
|
||||
|
||||
struct parts *child;
|
||||
PARTS_FOREACH_CHILD(child, parts) {
|
||||
int child_w, child_h;
|
||||
parts_get_layout_size(child, &child_w, &child_h);
|
||||
int cw = child->margin_left + child_w + child->margin_right;
|
||||
int ch = child->margin_top + child_h + child->margin_bottom;
|
||||
|
||||
// The layout box forces the child's origin_mode to match its own align.
|
||||
if (child->origin_mode != align) {
|
||||
child->origin_mode = align;
|
||||
parts_recalculate_hitbox(child);
|
||||
}
|
||||
|
||||
if (lb->layout_type == PARTS_LAYOUT_HORIZONTAL) {
|
||||
if (lb->wrap && abs((cur_x + cw * x_dir) - start_x) > lb->wrap_size
|
||||
&& max_cross > 0) {
|
||||
cur_y += max_cross * y_dir;
|
||||
max_cross = 0;
|
||||
cur_x = start_x;
|
||||
}
|
||||
} else {
|
||||
if (lb->wrap && abs((cur_y + ch * y_dir) - start_y) > lb->wrap_size
|
||||
&& max_cross > 0) {
|
||||
cur_x += max_cross * x_dir;
|
||||
max_cross = 0;
|
||||
cur_y = start_y;
|
||||
}
|
||||
}
|
||||
|
||||
// margin offset depends on alignment direction
|
||||
int mx = is_left ? child->margin_left : is_right ? -child->margin_right : 0;
|
||||
int my = is_top ? child->margin_top : is_bottom ? -child->margin_bottom : 0;
|
||||
|
||||
int pos_x = cur_x + mx;
|
||||
int pos_y = cur_y + my;
|
||||
if (child->local.pos.x != pos_x || child->local.pos.y != pos_y) {
|
||||
child->local.pos.x = pos_x;
|
||||
child->local.pos.y = pos_y;
|
||||
parts_recalculate_hitbox(child);
|
||||
parts_component_dirty(child);
|
||||
}
|
||||
|
||||
if (lb->layout_type == PARTS_LAYOUT_HORIZONTAL) {
|
||||
cur_x += cw * x_dir;
|
||||
if (ch > max_cross)
|
||||
max_cross = ch;
|
||||
} else {
|
||||
cur_y += ch * y_dir;
|
||||
if (cw > max_cross)
|
||||
max_cross = cw;
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
-2
@@ -174,6 +174,8 @@ static void parts_state_free(struct parts_state *state)
|
||||
{
|
||||
switch (state->type) {
|
||||
case PARTS_UNINITIALIZED:
|
||||
case PARTS_RECT_DETECTION:
|
||||
case PARTS_LAYOUT_BOX:
|
||||
break;
|
||||
case PARTS_CG:
|
||||
gfx_delete_texture(&state->common.texture);
|
||||
@@ -213,8 +215,6 @@ static void parts_state_free(struct parts_state *state)
|
||||
if (state->movie.sprite_no >= 0)
|
||||
sact_SP_Delete(state->movie.sprite_no);
|
||||
break;
|
||||
case PARTS_RECT_DETECTION:
|
||||
break;
|
||||
}
|
||||
memset(state, 0, sizeof(struct parts_state));
|
||||
}
|
||||
@@ -266,6 +266,10 @@ void parts_state_reset(struct parts_state *state, enum parts_type type)
|
||||
case PARTS_MOVIE:
|
||||
state->movie.sprite_no = -1;
|
||||
break;
|
||||
case PARTS_LAYOUT_BOX:
|
||||
state->layout_box.layout_type = PARTS_LAYOUT_VERTICAL;
|
||||
state->layout_box.align = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,6 +353,14 @@ struct parts_movie *parts_get_movie(struct parts *parts, int state)
|
||||
return &parts->states[state].movie;
|
||||
}
|
||||
|
||||
struct parts_layout_box *parts_get_layout_box(struct parts *parts)
|
||||
{
|
||||
if (parts->states[0].type != PARTS_LAYOUT_BOX) {
|
||||
parts_state_reset(&parts->states[0], PARTS_LAYOUT_BOX);
|
||||
}
|
||||
return &parts->states[0].layout_box;
|
||||
}
|
||||
|
||||
static Point calculate_offset(int mode, int w, int h)
|
||||
{
|
||||
switch (mode) {
|
||||
@@ -1077,6 +1089,8 @@ static void parts_update_component(struct parts *parts)
|
||||
parts->dirty = false;
|
||||
}
|
||||
|
||||
parts_do_layout(parts);
|
||||
|
||||
struct parts *child;
|
||||
PARTS_FOREACH_CHILD(child, parts) {
|
||||
parts_update_component(child);
|
||||
@@ -1099,6 +1113,10 @@ void PE_UpdateComponent(possibly_unused int passed_time)
|
||||
}
|
||||
parts->parent = parent;
|
||||
TAILQ_INSERT_TAIL(&parent->children, parts, child_list_entry);
|
||||
|
||||
// if parent is layout box, mark it dirty so that it can re-layout its children
|
||||
if (parent->states[0].type == PARTS_LAYOUT_BOX)
|
||||
parts_component_dirty(parent);
|
||||
}
|
||||
// TODO: should the child be orphaned if it already has a parent and an invalid
|
||||
// parent no is given?
|
||||
@@ -1931,6 +1949,7 @@ void PE_SetComponentType(int parts_no, int type, int state)
|
||||
struct parts *parts = parts_get(parts_no);
|
||||
enum parts_type pt = PARTS_UNINITIALIZED;
|
||||
switch (type) {
|
||||
case 8: pt = PARTS_LAYOUT_BOX; break;
|
||||
case 11: pt = PARTS_CG; break;
|
||||
case 12: pt = PARTS_ANIMATION; break;
|
||||
case 13: pt = PARTS_TEXT; break;
|
||||
@@ -1957,6 +1976,7 @@ int PE_GetComponentType(int parts_no, int state)
|
||||
return -1;
|
||||
|
||||
switch (parts->states[state].type) {
|
||||
case PARTS_LAYOUT_BOX: return 8;
|
||||
case PARTS_UNINITIALIZED: // defaluts to CG
|
||||
case PARTS_CG:
|
||||
return 11;
|
||||
|
||||
+34
-11
@@ -106,7 +106,8 @@ enum parts_type {
|
||||
PARTS_FLAT,
|
||||
PARTS_MOVIE,
|
||||
PARTS_RECT_DETECTION,
|
||||
#define PARTS_NR_TYPES (PARTS_RECT_DETECTION+1)
|
||||
PARTS_LAYOUT_BOX,
|
||||
#define PARTS_NR_TYPES (PARTS_LAYOUT_BOX+1)
|
||||
};
|
||||
|
||||
struct parts_common {
|
||||
@@ -320,6 +321,29 @@ struct parts_movie {
|
||||
int sprite_no; // SACT sprite number used as movie render target
|
||||
};
|
||||
|
||||
enum parts_layout_type {
|
||||
PARTS_LAYOUT_FREE = 0, // no automatic layout
|
||||
PARTS_LAYOUT_VERTICAL = 1,
|
||||
PARTS_LAYOUT_HORIZONTAL = 2,
|
||||
};
|
||||
|
||||
// In AliceSoft's PartsEngine implementation, LayoutBox is a component type
|
||||
// that is NOT per-state. We store it per-state for uniformity with other
|
||||
// component types, but parts_get_layout_box() and all code in layoutbox.c
|
||||
// operate on states[0] only. This is safe as long as game scripts never
|
||||
// convert a LayoutBox parts to/from another component type.
|
||||
struct parts_layout_box {
|
||||
struct parts_common common;
|
||||
enum parts_layout_type layout_type;
|
||||
bool wrap;
|
||||
int wrap_size;
|
||||
int align;
|
||||
int padding_top;
|
||||
int padding_bottom;
|
||||
int padding_left;
|
||||
int padding_right;
|
||||
};
|
||||
|
||||
struct parts_state {
|
||||
enum parts_type type;
|
||||
union {
|
||||
@@ -333,6 +357,7 @@ struct parts_state {
|
||||
struct parts_flash flash;
|
||||
struct parts_flat flat;
|
||||
struct parts_movie movie;
|
||||
struct parts_layout_box layout_box;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -379,6 +404,10 @@ struct parts {
|
||||
int draw_filter;
|
||||
bool message_window;
|
||||
int alpha_clipper_parts_no;
|
||||
int margin_top;
|
||||
int margin_bottom;
|
||||
int margin_left;
|
||||
int margin_right;
|
||||
struct parts_motion_list motion;
|
||||
int controller_no;
|
||||
};
|
||||
@@ -414,6 +443,7 @@ struct parts_construction_process *parts_get_construction_process(struct parts *
|
||||
struct parts_flash *parts_get_flash(struct parts *parts, int state);
|
||||
struct parts_flat *parts_get_flat(struct parts *parts, int state);
|
||||
struct parts_movie *parts_get_movie(struct parts *parts, int state);
|
||||
struct parts_layout_box *parts_get_layout_box(struct parts *parts);
|
||||
void parts_set_pos(struct parts *parts, Point pos);
|
||||
void parts_set_global_pos(Point pos);
|
||||
void parts_set_dims(struct parts *parts, struct parts_common *common, int w, int h);
|
||||
@@ -508,6 +538,9 @@ bool parts_flat_load(struct parts *parts, struct parts_flat *f, struct string *f
|
||||
bool parts_flat_update(struct parts_flat *f, int passed_time);
|
||||
int parts_flat_find_library(struct flat *fl, const char *name);
|
||||
|
||||
// layoutbox.c
|
||||
void parts_do_layout(struct parts *parts);
|
||||
|
||||
// debug.c
|
||||
struct sprite;
|
||||
void parts_debug_init(void);
|
||||
@@ -519,14 +552,4 @@ static inline bool parts_state_valid(int state)
|
||||
return state >= 0 && state <= 2;
|
||||
}
|
||||
|
||||
static inline int parts_get_width(struct parts *parts)
|
||||
{
|
||||
return parts->states[parts->state].common.w;
|
||||
}
|
||||
|
||||
static inline int parts_get_height(struct parts *parts)
|
||||
{
|
||||
return parts->states[parts->state].common.h;
|
||||
}
|
||||
|
||||
#endif /* SYSTEM4_PARTS_INTERNAL_H */
|
||||
|
||||
+2
-2
@@ -411,6 +411,8 @@ void parts_render(struct parts *parts)
|
||||
struct parts_state *state = &parts->states[parts->state];
|
||||
switch (state->type) {
|
||||
case PARTS_UNINITIALIZED:
|
||||
case PARTS_RECT_DETECTION:
|
||||
case PARTS_LAYOUT_BOX:
|
||||
break;
|
||||
case PARTS_CG:
|
||||
case PARTS_ANIMATION:
|
||||
@@ -431,8 +433,6 @@ void parts_render(struct parts *parts)
|
||||
case PARTS_FLAT:
|
||||
parts_render_flat(parts, &state->flat);
|
||||
break;
|
||||
case PARTS_RECT_DETECTION:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+41
-4
@@ -350,6 +350,30 @@ static void load_parts_flat(struct iarray_reader *r, struct parts *parts,
|
||||
flat->needs_advance = true;
|
||||
}
|
||||
|
||||
static void save_parts_layout_box(struct iarray_writer *w, struct parts_layout_box *lb)
|
||||
{
|
||||
iarray_write(w, lb->layout_type);
|
||||
iarray_write(w, lb->wrap);
|
||||
iarray_write(w, lb->wrap_size);
|
||||
iarray_write(w, lb->align);
|
||||
iarray_write(w, lb->padding_top);
|
||||
iarray_write(w, lb->padding_bottom);
|
||||
iarray_write(w, lb->padding_left);
|
||||
iarray_write(w, lb->padding_right);
|
||||
}
|
||||
|
||||
static void load_parts_layout_box(struct iarray_reader *r, struct parts_layout_box *lb)
|
||||
{
|
||||
lb->layout_type = iarray_read(r);
|
||||
lb->wrap = iarray_read(r);
|
||||
lb->wrap_size = iarray_read(r);
|
||||
lb->align = iarray_read(r);
|
||||
lb->padding_top = iarray_read(r);
|
||||
lb->padding_bottom = iarray_read(r);
|
||||
lb->padding_left = iarray_read(r);
|
||||
lb->padding_right = iarray_read(r);
|
||||
}
|
||||
|
||||
static void save_parts_state(struct iarray_writer *w, struct parts_state *state)
|
||||
{
|
||||
iarray_write(w, state->type);
|
||||
@@ -360,6 +384,8 @@ static void save_parts_state(struct iarray_writer *w, struct parts_state *state)
|
||||
iarray_write_rectangle(w, &state->common.surface_area);
|
||||
switch (state->type) {
|
||||
case PARTS_UNINITIALIZED:
|
||||
case PARTS_MOVIE:
|
||||
case PARTS_RECT_DETECTION:
|
||||
break;
|
||||
case PARTS_CG:
|
||||
save_parts_cg(w, &state->cg);
|
||||
@@ -386,8 +412,8 @@ static void save_parts_state(struct iarray_writer *w, struct parts_state *state)
|
||||
case PARTS_FLAT:
|
||||
save_parts_flat(w, &state->flat);
|
||||
break;
|
||||
case PARTS_MOVIE:
|
||||
case PARTS_RECT_DETECTION:
|
||||
case PARTS_LAYOUT_BOX:
|
||||
save_parts_layout_box(w, &state->layout_box);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -403,6 +429,8 @@ static void load_parts_state(struct iarray_reader *r, struct parts *parts,
|
||||
iarray_read_rectangle(r, &state->common.surface_area);
|
||||
switch (state->type) {
|
||||
case PARTS_UNINITIALIZED:
|
||||
case PARTS_MOVIE:
|
||||
case PARTS_RECT_DETECTION:
|
||||
break;
|
||||
case PARTS_CG:
|
||||
load_parts_cg(r, parts, &state->cg);
|
||||
@@ -431,8 +459,8 @@ static void load_parts_state(struct iarray_reader *r, struct parts *parts,
|
||||
case PARTS_FLAT:
|
||||
load_parts_flat(r, parts, &state->flat);
|
||||
break;
|
||||
case PARTS_MOVIE:
|
||||
case PARTS_RECT_DETECTION:
|
||||
case PARTS_LAYOUT_BOX:
|
||||
load_parts_layout_box(r, &state->layout_box);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -538,6 +566,10 @@ static void save_parts(struct iarray_writer *w, struct parts *parts)
|
||||
iarray_write(w, parts->controller_no);
|
||||
iarray_write(w, parts->pass_cursor);
|
||||
iarray_write(w, parts->lock_input_state);
|
||||
iarray_write(w, parts->margin_top);
|
||||
iarray_write(w, parts->margin_bottom);
|
||||
iarray_write(w, parts->margin_left);
|
||||
iarray_write(w, parts->margin_right);
|
||||
}
|
||||
|
||||
unsigned motion_count_pos = iarray_writer_pos(w);
|
||||
@@ -584,7 +616,12 @@ static void load_parts(struct iarray_reader *r, int version)
|
||||
parts->controller_no = iarray_read(r);
|
||||
parts->pass_cursor = iarray_read(r);
|
||||
parts->lock_input_state = iarray_read(r);
|
||||
parts->margin_top = iarray_read(r);
|
||||
parts->margin_bottom = iarray_read(r);
|
||||
parts->margin_left = iarray_read(r);
|
||||
parts->margin_right = iarray_read(r);
|
||||
}
|
||||
|
||||
int motion_count = iarray_read(r);
|
||||
for (int i = 0; i < motion_count; i++) {
|
||||
struct parts_motion *motion = load_parts_motion(r);
|
||||
|
||||
Reference in New Issue
Block a user