PartsEngine: Implement drag-and-drop processing

This implements Parts_SetDrag and drag/drop event handling. When a part
has drag enabled, clicking and moving the mouse will move the part and
emit DRAG_BEGIN/DRAGGING/DRAG_END messages. Drop target detection emits
DROP_ENTER/DROP_ON/DROPPED/DROP_LEAVE messages on parts under the cursor
during drag.
This commit is contained in:
kichikuou
2026-04-25 07:38:46 +09:00
parent 15f70f29a0
commit 1b87d3f9a5
6 changed files with 110 additions and 1 deletions
+1
View File
@@ -172,6 +172,7 @@ void PE_SetPassCursor(int parts_no, bool pass);
bool PE_GetPartsPassCursor(int parts_no);
void PE_SetClickable(int parts_no, bool clickable);
bool PE_GetPartsClickable(int parts_no);
void PE_SetDrag(int parts_no, bool enable);
void PE_SetPartsGroupDecideOnCursor(int group_no, bool decide_on_cursor);
void PE_SetPartsGroupDecideClick(int group_no, bool decide_click);
void PE_SetOnCursorShowLinkPartsNumber(int parts_no, int link_parts_no);
+1 -1
View File
@@ -837,7 +837,7 @@ HLL_LIBRARY(PartsEngine,
HLL_EXPORT(Parts_SetPassCursor, PE_SetPassCursor),
HLL_EXPORT(Parts_SetClickable, PE_SetClickable),
HLL_TODO_EXPORT(Parts_SetResetTimerByChangeInputStatus, PartsEngine_Parts_SetResetTimerByChangeInputStatus),
HLL_TODO_EXPORT(Parts_SetDrag, PartsEngine_Parts_SetDrag),
HLL_EXPORT(Parts_SetDrag, PE_SetDrag),
HLL_EXPORT(Parts_SetParentPartsNumber, PE_SetParentPartsNumber),
HLL_EXPORT(Parts_SetInputState, PE_SetInputState),
HLL_EXPORT(Parts_SetOnCursorShowLinkPartsNumber, PE_SetOnCursorShowLinkPartsNumber),
+99
View File
@@ -34,6 +34,13 @@ static int clicked_parts = 0;
// the current (partially) clicked parts number
static int click_down_parts = 0;
// drag state
static struct parts *drag_parts = NULL;
static bool is_dragging = false;
static Point drag_initial_pos;
static Point drag_start_cursor;
static struct parts *drop_target = NULL;
static bool parts_hittest(struct parts *parts, int state, Point pos)
{
Rectangle hitbox = parts->states[state].common.hitbox;
@@ -44,6 +51,21 @@ static bool parts_hittest(struct parts *parts, int state, Point pos)
return SDL_PointInRect(&pos, &hitbox);
}
static void drag_state_reset(void)
{
drag_parts = NULL;
is_dragging = false;
drop_target = NULL;
}
void parts_input_reset_drag(struct parts *parts)
{
if (parts == drag_parts)
drag_state_reset();
else if (parts == drop_target)
drop_target = NULL;
}
static void parts_update_mouse(struct parts *parts, Point cur_pos, bool cur_clicking,
int passed_time, bool *hover_consumed, bool *click_consumed)
{
@@ -106,6 +128,10 @@ static void parts_update_mouse(struct parts *parts, Point cur_pos, bool cur_clic
click_down_parts = parts->no;
*click_consumed = true;
drag_parts = parts;
drag_initial_pos = parts->local.pos;
drag_start_cursor = cur_pos;
// KEY_TRIGGER message fires on press transition
parts_msg_push(parts->no, parts->delegate_index,
PARTS_MSG_KEY_TRIGGER, "i", VK_LBUTTON);
@@ -154,7 +180,74 @@ void PE_UpdateInputState(int passed_time)
&hover_consumed, &click_consumed);
}
// Drag movement processing
if (drag_parts && cur_clicking) {
bool cursor_moved = (cur_pos.x != parts_prev_pos.x ||
cur_pos.y != parts_prev_pos.y);
if (cursor_moved && drag_parts->draggable) {
Point new_pos = {
drag_initial_pos.x + (cur_pos.x - drag_start_cursor.x),
drag_initial_pos.y + (cur_pos.y - drag_start_cursor.y)
};
parts_set_pos(drag_parts, new_pos);
if (!is_dragging) {
parts_msg_push(drag_parts->no,
drag_parts->delegate_index,
PARTS_MSG_DRAG_BEGIN, "");
}
parts_msg_push(drag_parts->no, drag_parts->delegate_index,
PARTS_MSG_DRAGGING, "iiii", drag_start_cursor.x, drag_start_cursor.y,
cur_pos.x, cur_pos.y);
is_dragging = true;
}
// Drop target tracking
if (cursor_moved && is_dragging) {
struct parts *new_drop = NULL;
PARTS_LIST_FOREACH_REVERSE(parts) {
if (parts == drag_parts)
continue;
if (parts_hittest(parts, PARTS_STATE_DEFAULT, cur_pos)) {
new_drop = parts;
if (!parts->pass_cursor)
break;
}
}
if (new_drop != drop_target) {
if (drop_target) {
parts_msg_push(drop_target->no,
drop_target->delegate_index,
PARTS_MSG_DROP_LEAVE, "i", drag_parts->no);
}
if (new_drop) {
parts_msg_push(new_drop->no,
new_drop->delegate_index,
PARTS_MSG_DROP_ENTER, "i", drag_parts->no);
}
drop_target = new_drop;
} else if (drop_target) {
parts_msg_push(drop_target->no,
drop_target->delegate_index,
PARTS_MSG_DROP_ON, "iii", drag_parts->no, cur_pos.x,
cur_pos.y);
}
}
}
// Release handling
if (prev_clicking && !cur_clicking) {
if (is_dragging && drag_parts && drag_parts->draggable) {
parts_msg_push(drag_parts->no, drag_parts->delegate_index,
PARTS_MSG_DRAG_END, "");
}
if (drop_target && drag_parts) {
parts_msg_push(drop_target->no, drop_target->delegate_index,
PARTS_MSG_DROPPED, "iii", drag_parts->no, cur_pos.x, cur_pos.y);
parts_msg_push(drop_target->no, drop_target->delegate_index,
PARTS_MSG_DROP_LEAVE, "i", drag_parts->no);
}
drag_state_reset();
if (!click_down_parts) {
// TODO: play misclick sound
}
@@ -247,6 +340,12 @@ void PE_EndInput(void)
{
parts_began_click = false;
clicked_parts = 0;
drag_state_reset();
}
void PE_SetDrag(int parts_no, bool enable)
{
parts_get(parts_no)->draggable = !!enable;
}
int PE_GetClickPartsNumber(void)
+1
View File
@@ -970,6 +970,7 @@ void parts_release(int parts_no)
return;
struct parts *parts = slot->value;
parts_input_reset_drag(parts);
parts_clear_motion(parts);
for (int i = 0; i < PARTS_NR_STATES; i++) {
parts_state_free(&parts->states[i]);
+6
View File
@@ -393,6 +393,7 @@ struct parts {
bool pass_cursor;
bool lock_input_state;
bool want_save;
bool draggable;
int on_cursor_sound;
int on_click_sound;
int origin_mode;
@@ -500,6 +501,7 @@ void parts_add_motion(struct parts *parts, struct parts_motion *motion);
// input.c
extern bool parts_began_click;
void parts_input_reset_drag(struct parts *parts);
// message.c
enum parts_message_type {
@@ -512,6 +514,10 @@ enum parts_message_type {
PARTS_MSG_DRAG_BEGIN = 7,
PARTS_MSG_DRAGGING = 8,
PARTS_MSG_DRAG_END = 9,
PARTS_MSG_DROP_ENTER = 10,
PARTS_MSG_DROP_ON = 11,
PARTS_MSG_DROPPED = 12,
PARTS_MSG_DROP_LEAVE = 13,
PARTS_MSG_KEY_TRIGGER = 14,
PARTS_MSG_KEY_DOWN = 15,
PARTS_MSG_KEY_UP = 17,
+2
View File
@@ -570,6 +570,7 @@ static void save_parts(struct iarray_writer *w, struct parts *parts)
iarray_write(w, parts->margin_bottom);
iarray_write(w, parts->margin_left);
iarray_write(w, parts->margin_right);
iarray_write(w, parts->draggable);
}
unsigned motion_count_pos = iarray_writer_pos(w);
@@ -620,6 +621,7 @@ static void load_parts(struct iarray_reader *r, int version)
parts->margin_bottom = iarray_read(r);
parts->margin_left = iarray_read(r);
parts->margin_right = iarray_read(r);
parts->draggable = iarray_read(r);
}
int motion_count = iarray_read(r);