1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
13 #include "lib/scritchui/core/core.h"
14 #include "lib/scritchui/scritchuiTypes.h"
15 #include "sjme/debug.h"
17 static sjme_errorCode
sjme_scritchui_baseInputListenerMouse(
18 sjme_attrInNotNull sjme_scritchui inState
,
19 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
20 sjme_attrInNotNull
const sjme_scritchinput_event
* inEvent
)
22 sjme_scritchui_listener_input
* infoUser
;
23 sjme_scritchui_uiMouseState
* currentMouse
;
24 sjme_scritchui_uiMouseState
* logicalMouse
;
25 sjme_scritchinput_event emit
;
26 sjme_jint buttonChange
, shift
;
27 sjme_jboolean pressed
;
29 if (inState
== NULL
|| inComponent
== NULL
|| inEvent
== NULL
)
30 return SJME_ERROR_NULL_ARGUMENTS
;
32 /* We are adjusting with multiple mouse states. */
33 currentMouse
= &inComponent
->state
.mouse
[0];
34 logicalMouse
= &inComponent
->state
.mouse
[1];
36 /* Get callback information. */
37 infoUser
= &SJME_SCRITCHUI_LISTENER_USER(inComponent
, input
);
39 /* Always copy modifiers over. */
40 logicalMouse
->mouseModifiers
= currentMouse
->mouseModifiers
;
42 /* Has there been a change to buttons? */
43 if (currentMouse
->mouseButtons
!= logicalMouse
->mouseButtons
)
45 /* Store new position, if valid. */
46 if (currentMouse
->mouseX
!= 0)
47 logicalMouse
->mouseX
= currentMouse
->mouseX
;
48 if (currentMouse
->mouseY
!= 0)
49 logicalMouse
->mouseY
= currentMouse
->mouseY
;
51 /* Determine the buttons that changed. */
52 buttonChange
= (currentMouse
->mouseButtons
^
53 logicalMouse
->mouseButtons
);
55 /* If anything changed, go through each button. */
56 for (shift
= 0; buttonChange
!= 0 && shift
<= 31; shift
++)
58 /* Button did not change? */
59 int mask
= (1 << shift
);
60 if ((buttonChange
& mask
) == 0)
63 /* We are changing this, so flip to new state. */
64 logicalMouse
->mouseButtons
^= mask
;
66 /* Is this being pressed or released? */
67 pressed
= ((logicalMouse
->mouseButtons
& mask
) != 0);
70 memset(&emit
, 0, sizeof(emit
));
72 emit
.type
= SJME_SCRITCHINPUT_TYPE_MOUSE_BUTTON_PRESSED
;
74 emit
.type
= SJME_SCRITCHINPUT_TYPE_MOUSE_BUTTON_RELEASED
;
75 emit
.time
= inEvent
->time
;
76 emit
.data
.mouseButton
.button
= shift
+ 1;
77 emit
.data
.mouseButton
.buttonMask
= logicalMouse
->mouseButtons
;
78 emit
.data
.mouseButton
.modifiers
= logicalMouse
->mouseModifiers
;
79 emit
.data
.mouseButton
.x
= logicalMouse
->mouseX
;
80 emit
.data
.mouseButton
.y
= logicalMouse
->mouseY
;
84 sjme_message("Mouse Button: %s %d %08x (%d, %d) [sh=%d, bc=%08x]",
85 (pressed
? "pressed" : "released"),
86 emit
.data
.mouseButton
.buttonMask
,
87 emit
.data
.mouseButton
.button
,
88 emit
.data
.mouseButton
.x
,
89 emit
.data
.mouseButton
.y
,
93 /* Emit a button event. */
94 if (infoUser
->callback
!= NULL
)
95 return infoUser
->callback(inState
, inComponent
, &emit
);
99 /* Has there been a change of position? */
100 if (currentMouse
->mouseX
!= logicalMouse
->mouseX
||
101 currentMouse
->mouseY
!= logicalMouse
->mouseY
)
103 /* Store new position. */
104 logicalMouse
->mouseX
= currentMouse
->mouseX
;
105 logicalMouse
->mouseY
= currentMouse
->mouseY
;
108 memset(&emit
, 0, sizeof(emit
));
109 emit
.type
= SJME_SCRITCHINPUT_TYPE_MOUSE_MOTION
;
110 emit
.time
= inEvent
->time
;
111 emit
.data
.mouseMotion
.buttonMask
= logicalMouse
->mouseButtons
;
112 emit
.data
.mouseMotion
.modifiers
= logicalMouse
->mouseModifiers
;
113 emit
.data
.mouseMotion
.x
= logicalMouse
->mouseX
;
114 emit
.data
.mouseMotion
.y
= logicalMouse
->mouseY
;
118 sjme_message("Mouse Motion: %08x (%d, %d)",
119 emit
.data
.mouseMotion
.buttonMask
,
120 emit
.data
.mouseMotion
.x
,
121 emit
.data
.mouseMotion
.y
);
124 /* Emit a motion event. */
125 if (infoUser
->callback
!= NULL
)
126 return infoUser
->callback(inState
, inComponent
, &emit
);
129 /* Nothing changed, we want to reduce duplication. */
130 return SJME_ERROR_NONE
;
133 static sjme_errorCode
sjme_scritchui_baseInputListener(
134 sjme_attrInNotNull sjme_scritchui inState
,
135 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
136 sjme_attrInNotNull
const sjme_scritchinput_event
* inEvent
)
138 sjme_scritchui_listener_input
* infoUser
;
139 sjme_scritchui_uiMouseState
* currentMouse
;
140 sjme_scritchui_uiMouseState
* logicalMouse
;
141 sjme_scritchinput_event clone
;
143 sjme_jboolean isPressEvent
;
145 if (inState
== NULL
|| inComponent
== NULL
|| inEvent
== NULL
)
146 return SJME_ERROR_NULL_ARGUMENTS
;
148 /* Get callback information. */
149 infoUser
= &SJME_SCRITCHUI_LISTENER_USER(inComponent
, input
);
151 /* Clone event data for normalization. */
152 memmove(&clone
, inEvent
, sizeof(clone
));
154 /* We are only adjusting the current mouse state. */
155 currentMouse
= &inComponent
->state
.mouse
[0];
156 logicalMouse
= &inComponent
->state
.mouse
[1];
159 if (clone
.type
== SJME_SCRITCHINPUT_TYPE_MOUSE_MOTION
||
160 clone
.type
== SJME_SCRITCHINPUT_TYPE_MOUSE_BUTTON_PRESSED
||
161 clone
.type
== SJME_SCRITCHINPUT_TYPE_MOUSE_BUTTON_RELEASED
)
163 /* Is this a press/release event? */
164 isPressEvent
= SJME_JNI_FALSE
;
165 if (clone
.type
== SJME_SCRITCHINPUT_TYPE_MOUSE_BUTTON_PRESSED
||
166 clone
.type
== SJME_SCRITCHINPUT_TYPE_MOUSE_BUTTON_RELEASED
)
167 isPressEvent
= SJME_JNI_TRUE
;
169 /* Pull from logical unless position is set as some */
170 /* GUIs do not report position when buttons are pressed. This is */
171 /* the case where mouse buttons are technically keyboard keys. */
172 /* Motion always copies, however. */
173 if (isPressEvent
&& clone
.data
.mouseButton
.x
== 0)
174 currentMouse
->mouseX
= logicalMouse
->mouseX
;
176 currentMouse
->mouseX
= clone
.data
.mouseButton
.x
;
178 if (isPressEvent
&& clone
.data
.mouseButton
.y
== 0)
179 currentMouse
->mouseY
= logicalMouse
->mouseY
;
181 currentMouse
->mouseY
= clone
.data
.mouseButton
.y
;
183 /* Set modifiers, if unsupported for this event type then pull */
184 /* from the last logical modifier state. */
185 if (clone
.data
.mouseMotion
.modifiers
==
186 SJME_SCRITCHINPUT_MODIFIER_UNSUPPORTED
)
187 currentMouse
->mouseModifiers
= logicalMouse
->mouseModifiers
;
189 currentMouse
->mouseModifiers
= clone
.data
.mouseMotion
.modifiers
;
191 /* If no buttons are down, pull from logical. Otherwise, use the */
192 /* mask from the event. Some GUIs will not pass buttons during */
193 /* motion events, but will for normal press/release. */
194 if (clone
.data
.mouseMotion
.buttonMask
== 0)
195 currentMouse
->mouseButtons
= logicalMouse
->mouseButtons
;
197 currentMouse
->mouseButtons
= clone
.data
.mouseMotion
.buttonMask
;
199 /* Either set or clear the mouse button bit, if known. */
200 if (isPressEvent
&& clone
.data
.mouseButton
.button
!= 0)
202 bit
= (1 << (clone
.data
.mouseButton
.button
- 1));
203 if (clone
.type
== SJME_SCRITCHINPUT_TYPE_MOUSE_BUTTON_PRESSED
)
204 currentMouse
->mouseButtons
|= bit
;
206 currentMouse
->mouseButtons
&= ~bit
;
209 /* Forward to handler to further clean up. */
210 return sjme_scritchui_baseInputListenerMouse(inState
, inComponent
,
215 else if (clone
.type
== SJME_SCRITCHINPUT_TYPE_KEY_PRESSED
||
216 clone
.type
== SJME_SCRITCHINPUT_TYPE_KEY_RELEASED
)
218 /* If any lowercase keys were passed, make them capitalized. */
219 bit
= clone
.data
.key
.code
;
220 if (bit
>= 'a' && bit
<= 'z')
221 clone
.data
.key
.code
= 'A' + (bit
- 'a');
224 /* Forward to callback! */
225 if (infoUser
->callback
!= NULL
)
226 return infoUser
->callback(inState
, inComponent
, &clone
);
227 return SJME_ERROR_NONE
;
230 static sjme_errorCode
sjme_scritchui_basePaintListener(
231 sjme_attrInNotNull sjme_scritchui inState
,
232 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
233 sjme_attrInNotNull sjme_scritchui_pencil g
,
234 sjme_attrInPositive sjme_jint sw
,
235 sjme_attrInPositive sjme_jint sh
,
236 sjme_attrInValue sjme_jint special
)
238 sjme_errorCode error
;
239 sjme_scritchui_uiPaintable paint
;
240 sjme_scritchui_listener_paint
* infoUser
;
241 sjme_scritchui_paintListenerFunc callback
;
243 if (inState
== NULL
|| inComponent
== NULL
|| g
== NULL
)
244 return SJME_ERROR_NULL_ARGUMENTS
;
246 /* Not something we can paint? */
248 if (sjme_error_is(error
= inState
->intern
->getPaintable(inState
,
249 inComponent
, &paint
)) || paint
== NULL
)
250 return sjme_error_defaultOr(error
,
251 SJME_ERROR_INVALID_ARGUMENT
);
254 infoUser
= &SJME_SCRITCHUI_LISTENER_USER(paint
, paint
);
256 /* No actual paint listener? */
257 callback
= infoUser
->callback
;
258 if (callback
== NULL
)
260 error
= SJME_ERROR_NO_LISTENER
;
261 goto fail_noListener
;
264 /* Forward to callback. */
265 sjme_atomic_sjme_jint_set(&paint
->inPaint
, 1);
266 error
= callback(inState
, inComponent
, g
, sw
, sh
, special
);
268 #if defined(SJME_CONFIG_DEBUG)
270 if (sjme_error_is(error
))
271 sjme_message("Paint failed: %d", error
);
274 /* No longer painting. */
275 sjme_atomic_sjme_jint_set(&paint
->inPaint
, 0);
277 /* Success or failure! */
279 paint
->lastError
= error
;
283 static sjme_errorCode
sjme_scritchui_core_baseActivateListener(
284 sjme_attrInNotNull sjme_scritchui inState
,
285 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
)
287 sjme_errorCode error
;
288 sjme_scritchui_listener_activate
* info
;
289 sjme_scritchui_activateListenerFunc callback
;
291 if (inState
== NULL
|| inComponent
== NULL
)
292 return SJME_ERROR_NULL_ARGUMENTS
;
295 info
= &SJME_SCRITCHUI_LISTENER_USER(inComponent
, activate
);
297 /* Call user handler, if there is one */
298 callback
= info
->callback
;
299 if (callback
!= NULL
)
300 if (sjme_error_is(error
= callback(inState
, inComponent
)))
301 return sjme_error_default(error
);
304 return SJME_ERROR_NONE
;
307 static sjme_errorCode
sjme_scritchui_core_baseSizeListener(
308 sjme_attrInNotNull sjme_scritchui inState
,
309 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
310 sjme_attrInPositiveNonZero sjme_jint newWidth
,
311 sjme_attrInPositiveNonZero sjme_jint newHeight
)
313 sjme_errorCode error
;
314 sjme_scritchui_listener_size
* info
;
315 sjme_scritchui_sizeListenerFunc callback
;
317 if (inState
== NULL
|| inComponent
== NULL
)
318 return SJME_ERROR_NULL_ARGUMENTS
;
321 info
= &SJME_SCRITCHUI_LISTENER_USER(inComponent
, size
);
323 /* Call user handler, if there is one */
324 callback
= info
->callback
;
325 if (callback
!= NULL
)
326 if (sjme_error_is(error
= callback(inState
, inComponent
,
327 newWidth
, newHeight
)))
328 return sjme_error_default(error
);
330 /* Schedule repaint, ignore any errors. */
331 inState
->api
->componentRepaint(inState
, inComponent
,
332 0, 0, INT32_MAX
, INT32_MAX
);
335 return SJME_ERROR_NONE
;
338 static sjme_errorCode
sjme_scritchui_core_baseVisibleListener(
339 sjme_attrInNotNull sjme_scritchui inState
,
340 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
341 sjme_attrInValue sjme_jboolean fromVisible
,
342 sjme_attrInValue sjme_jboolean toVisible
)
344 sjme_scritchui_listener_visible
* infoUser
;
345 sjme_jboolean wasVisible
;
346 sjme_jboolean wasUserVisible
;
348 if (inState
== NULL
|| inComponent
== NULL
)
349 return SJME_ERROR_NULL_ARGUMENTS
;
351 /* Has core visibility changed? We do not want to spam visibility */
352 /* changes to our components if there is no point to it. */
353 wasVisible
= inComponent
->state
.isVisible
;
354 if (toVisible
!= wasVisible
)
356 /* Update visibility state. */
357 inComponent
->state
.isVisible
= toVisible
;
360 /* There may be a delay before a listener is set for a user listener */
361 /* so wait until that occurs. */
362 wasUserVisible
= inComponent
->state
.isUserVisible
;
363 if (toVisible
!= wasUserVisible
)
365 /* Send to callback accordingly, if one is set. */
366 infoUser
= &SJME_SCRITCHUI_LISTENER_USER(inComponent
, visible
);
367 if (infoUser
->callback
!= NULL
)
370 inComponent
->state
.isUserVisible
= toVisible
;
372 return infoUser
->callback(inState
, inComponent
,
373 wasVisible
, toVisible
);
378 return SJME_ERROR_NONE
;
382 * Belayed repainting.
384 * @param anything The input component.
385 * @return On any error.
388 static sjme_thread_result sjme_attrThreadCall
389 sjme_scritchui_core_componentRepaintBelay(
390 sjme_attrInNullable sjme_thread_parameter anything
)
392 sjme_errorCode error
;
393 sjme_scritchui inState
;
394 sjme_scritchui_uiComponent inComponent
;
395 sjme_scritchui_uiPaintable paint
;
396 sjme_scritchui_rect rect
;
398 if (anything
== NULL
)
399 return SJME_THREAD_RESULT(SJME_ERROR_NULL_ARGUMENTS
);
401 /* Recover component and state. */
402 inComponent
= (sjme_scritchui_uiComponent
)anything
;
403 inState
= inComponent
->common
.state
;
405 /* Only certain types are paintable. */
407 if (sjme_error_is(error
= inState
->intern
->getPaintable(inState
,
408 inComponent
, &paint
)) || paint
== NULL
)
409 return SJME_THREAD_RESULT(sjme_error_default(error
));
411 /* Call paint now. */
412 rect
= paint
->belayRect
;
413 error
= inState
->impl
->componentRepaint(inState
, inComponent
,
414 rect
.s
.x
, rect
.s
.y
, rect
.d
.width
, rect
.d
.height
);
415 return SJME_THREAD_RESULT(error
);
418 sjme_errorCode
sjme_scritchui_core_componentFocusGrab(
419 sjme_attrInNotNull sjme_scritchui inState
,
420 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
)
422 sjme_errorCode error
;
424 if (inState
== NULL
|| inComponent
== NULL
)
425 return SJME_ERROR_NULL_ARGUMENTS
;
427 /* Not implemented? */
428 if (inState
->impl
->componentFocusGrab
== NULL
)
429 return sjme_error_notImplemented(0);
431 /* For Windows, keyboard input happens on the window itself and not */
432 /* the component, so we need to refer back. */
433 if (sjme_error_is(error
= inState
->intern
->bindFocus(inState
,
434 inComponent
, inComponent
, SJME_JNI_TRUE
)))
435 return sjme_error_default(error
);
437 /* Direct forward. */
438 return inState
->impl
->componentFocusGrab(inState
, inComponent
);
441 sjme_errorCode
sjme_scritchui_core_componentFocusHas(
442 sjme_attrInNotNull sjme_scritchui inState
,
443 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
444 sjme_attrOutNotNull sjme_jboolean
* outHasFocus
)
446 if (inState
== NULL
|| inComponent
== NULL
|| outHasFocus
== NULL
)
447 return SJME_ERROR_NULL_ARGUMENTS
;
449 /* Not implemented? */
450 if (inState
->impl
->componentFocusHas
== NULL
)
451 return sjme_error_notImplemented(0);
453 /* Direct forward. */
454 return inState
->impl
->componentFocusHas(inState
, inComponent
, outHasFocus
);
457 sjme_errorCode
sjme_scritchui_core_componentGetParent(
458 sjme_attrInNotNull sjme_scritchui inState
,
459 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
460 sjme_attrOutNotNull sjme_scritchui_uiComponent
* outParent
)
462 if (inState
== NULL
|| inComponent
== NULL
|| outParent
== NULL
)
463 return SJME_ERROR_NULL_ARGUMENTS
;
465 /* This is a simple read. */
466 *outParent
= inComponent
->parent
;
467 return SJME_ERROR_NONE
;
470 sjme_errorCode
sjme_scritchui_core_componentPosition(
471 sjme_attrInNotNull sjme_scritchui inState
,
472 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
473 sjme_attrOutNullable sjme_jint
* outX
,
474 sjme_attrOutNullable sjme_jint
* outY
)
476 if (inState
== NULL
|| inComponent
== NULL
||
477 (outX
== NULL
&& outY
== NULL
))
478 return SJME_ERROR_NULL_ARGUMENTS
;
480 /* If there is native position information, use it. */
481 if (inState
->impl
->componentPosition
!= NULL
)
482 return inState
->impl
->componentPosition(inState
, inComponent
,
485 /* Otherwise, fallback to last known bounds. */
487 *outX
= inComponent
->bounds
.s
.x
;
489 *outY
= inComponent
->bounds
.s
.y
;
492 return SJME_ERROR_NONE
;
495 sjme_errorCode
sjme_scritchui_core_componentRepaint(
496 sjme_attrInNotNull sjme_scritchui inState
,
497 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
498 sjme_attrInPositive sjme_jint x
,
499 sjme_attrInPositive sjme_jint y
,
500 sjme_attrInPositiveNonZero sjme_jint width
,
501 sjme_attrInPositiveNonZero sjme_jint height
)
503 sjme_errorCode error
;
504 sjme_scritchui_uiPaintable paint
;
505 sjme_scritchui_uiContainer container
;
506 sjme_scritchui_uiComponent
* subComponents
;
509 if (inState
== NULL
|| inComponent
== NULL
)
510 return SJME_ERROR_NULL_ARGUMENTS
;
512 /* Not implemented? */
513 if (inState
->impl
->componentRepaint
== NULL
)
514 return sjme_error_notImplemented(0);
516 /* Only certain types are paintable, ignore if requested. */
518 if (sjme_error_is(error
= inState
->intern
->getPaintable(inState
,
519 inComponent
, &paint
)) || paint
== NULL
)
521 /* An actual error? */
522 if (error
!= SJME_ERROR_INVALID_ARGUMENT
)
523 return sjme_error_default(error
);
526 /* If this is a container, repaint all children. */
528 if (sjme_error_is(error
= inState
->intern
->getContainer(inState
,
529 inComponent
, &container
)) || container
== NULL
)
530 return sjme_error_default(error
);
533 if (container
->components
== NULL
||
534 container
->components
->length
== 0)
535 return SJME_ERROR_NONE
;
537 /* Allocate storage for components. */
538 n
= container
->components
->length
;
539 subComponents
= sjme_alloca(sizeof(*subComponents
) * n
);
540 if (subComponents
== NULL
)
541 return SJME_ERROR_OUT_OF_MEMORY
;
543 /* Get all components. */
544 memmove(subComponents
, container
->components
->elements
,
545 sizeof(*subComponents
) * n
);
547 /* Go through each and request repainting. */
548 for (i
= 0; i
< n
; i
++)
549 if (subComponents
[i
] != NULL
)
550 if (sjme_error_is(error
= inState
->apiInThread
551 ->componentRepaint(inState
, subComponents
[i
],
552 x
, y
, width
, height
)))
554 if (error
!= SJME_ERROR_INVALID_ARGUMENT
)
555 return sjme_error_default(error
);
560 return SJME_ERROR_NONE
;
563 /* Rather than failing, just normalize. */
573 /* If we are in a paint, we need to delay painting by a single frame */
574 /* otherwise the native UI might get stuck not repainting or end up */
575 /* in an infinite loop. */
576 if (sjme_atomic_sjme_jint_get(&paint
->inPaint
) != 0)
578 /* Store paint properties. */
579 paint
->belayRect
.s
.x
= x
;
580 paint
->belayRect
.s
.y
= y
;
581 paint
->belayRect
.d
.width
= width
;
582 paint
->belayRect
.d
.height
= height
;
584 /* Schedule for later, if it errors fall through to paint. */
585 if (!sjme_error_is(inState
->api
->loopExecuteLater(inState
,
586 sjme_scritchui_core_componentRepaintBelay
,
588 return SJME_ERROR_NONE
;
592 return inState
->impl
->componentRepaint(inState
, inComponent
,
593 x
, y
, width
, height
);
596 sjme_errorCode
sjme_scritchui_core_componentRevalidate(
597 sjme_attrInNotNull sjme_scritchui inState
,
598 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
)
600 sjme_errorCode error
;
602 if (inState
== NULL
|| inComponent
== NULL
)
603 return SJME_ERROR_NULL_ARGUMENTS
;
605 if (inState
->impl
->componentRevalidate
== NULL
)
606 return sjme_error_notImplemented(0);
609 if (sjme_error_is(error
= inState
->impl
->componentRevalidate(inState
,
611 return sjme_error_default(error
);
614 return SJME_ERROR_NONE
;
617 sjme_errorCode
sjme_scritchui_core_componentSetActivateListener(
618 sjme_attrInNotNull sjme_scritchui inState
,
619 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
620 SJME_SCRITCHUI_SET_LISTENER_ARGS(activate
))
622 if (inState
== NULL
|| inComponent
== NULL
)
623 return SJME_ERROR_NULL_ARGUMENTS
;
625 return inState
->intern
->setSimpleListener(
627 (sjme_scritchui_listener_void
*)&SJME_SCRITCHUI_LISTENER_USER(
628 inComponent
, activate
),
629 (sjme_scritchui_voidListenerFunc
)inListener
,
633 sjme_errorCode
sjme_scritchui_core_componentSetInputListener(
634 sjme_attrInNotNull sjme_scritchui inState
,
635 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
636 SJME_SCRITCHUI_SET_LISTENER_ARGS(input
))
638 sjme_errorCode error
;
639 sjme_scritchui_inputListenerFunc coreListener
;
641 if (inState
== NULL
|| inComponent
== NULL
)
642 return SJME_ERROR_NULL_ARGUMENTS
;
644 /* Can only be set for panels. */
645 if (inComponent
->common
.type
!= SJME_SCRITCHUI_TYPE_PANEL
)
646 return SJME_ERROR_INVALID_ARGUMENT
;
648 /* Using core input listener? */
650 if (inListener
!= NULL
)
651 coreListener
= sjme_scritchui_baseInputListener
;
653 /* Set core listener events which is forwarded to for handling. */
654 if (inState
->impl
->componentSetInputListener
!= NULL
)
656 if (sjme_error_is(error
= inState
->impl
->
657 componentSetInputListener(inState
, inComponent
,
658 coreListener
, NULL
)))
659 return sjme_error_default(error
);
662 /* Set one regardless, there might be another way to handle. */
665 if (sjme_error_is(error
= inState
->intern
->setSimpleListener(
667 (sjme_scritchui_listener_void
*)&SJME_SCRITCHUI_LISTENER_CORE(
669 (sjme_scritchui_voidListenerFunc
)coreListener
,
671 return sjme_error_default(error
);
674 /* Set user listener. */
675 return inState
->intern
->setSimpleListener(
677 (sjme_scritchui_listener_void
*)&SJME_SCRITCHUI_LISTENER_USER(
679 (sjme_scritchui_voidListenerFunc
)inListener
,
683 sjme_errorCode
sjme_scritchui_core_componentSetPaintListener(
684 sjme_scritchui inState
,
685 sjme_scritchui_uiComponent inComponent
,
686 SJME_SCRITCHUI_SET_LISTENER_ARGS(paint
))
688 sjme_errorCode error
;
689 sjme_scritchui_uiPaintable paint
;
690 sjme_scritchui_listener_paint undo
;
691 sjme_scritchui_listener_paint
* infoCore
;
692 sjme_scritchui_listener_paint
* infoUser
;
693 sjme_scritchui_paintListenerFunc coreCallback
;
695 if (inState
== NULL
|| inComponent
== NULL
)
696 return SJME_ERROR_NULL_ARGUMENTS
;
699 if (inState
->impl
->componentSetPaintListener
== NULL
)
700 return sjme_error_notImplemented(0);
702 /* Only certain types can be painted on. */
704 if (sjme_error_is(error
= inState
->intern
->getPaintable(inState
,
705 inComponent
, &paint
)) || paint
== NULL
)
706 return sjme_error_default(error
);
708 /* Get listener information. */
709 infoUser
= &SJME_SCRITCHUI_LISTENER_USER(paint
, paint
);
710 infoCore
= &SJME_SCRITCHUI_LISTENER_CORE(paint
, paint
);
712 /* Copy data for undo. */
713 memmove(&undo
, infoUser
, sizeof(undo
));
715 /* Set new listener data. */
716 if (sjme_error_is(error
= inState
->intern
->setSimpleListener(
717 inState
, (sjme_scritchui_listener_void
*)infoUser
,
718 (sjme_scritchui_voidListenerFunc
)inListener
, copyFrontEnd
)))
719 return sjme_error_default(error
);
721 /* Which core callback is being used? */
722 coreCallback
= (inListener
!= NULL
? sjme_scritchui_basePaintListener
:
725 /* Is this callback changing? We need to set a new one! */
726 if (infoCore
->callback
!= coreCallback
)
728 if (sjme_error_is(error
=
729 inState
->impl
->componentSetPaintListener(inState
, inComponent
,
730 coreCallback
, NULL
)))
733 /* Set new listener data, if it was not changed. */
734 if (infoCore
->callback
!= coreCallback
)
735 if (sjme_error_is(error
= inState
->intern
->setSimpleListener(
736 inState
, (sjme_scritchui_listener_void
*)infoCore
,
737 (sjme_scritchui_voidListenerFunc
)inListener
,
739 return sjme_error_default(error
);
742 /* If there is a repaint handler, then run it but ignore any errors. */
743 if (inState
->apiInThread
->componentRepaint
!= NULL
)
744 inState
->apiInThread
->componentRepaint(inState
, inComponent
,
745 0, 0, INT32_MAX
, INT32_MAX
);
748 return SJME_ERROR_NONE
;
752 memmove(infoUser
, &undo
, sizeof(undo
));
754 return sjme_error_default(error
);
757 sjme_errorCode
sjme_scritchui_core_componentSetSizeListener(
758 sjme_attrInNotNull sjme_scritchui inState
,
759 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
760 SJME_SCRITCHUI_SET_LISTENER_ARGS(size
))
762 if (inState
== NULL
|| inComponent
== NULL
)
763 return SJME_ERROR_NULL_ARGUMENTS
;
765 return inState
->intern
->setSimpleListener(
767 (sjme_scritchui_listener_void
*)&SJME_SCRITCHUI_LISTENER_USER(
769 (sjme_scritchui_voidListenerFunc
)inListener
,
773 sjme_errorCode
sjme_scritchui_core_componentSetValueUpdateListener(
774 sjme_attrInNotNull sjme_scritchui inState
,
775 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
776 SJME_SCRITCHUI_SET_LISTENER_ARGS(valueUpdate
))
779 return sjme_error_notImplemented(0);
782 sjme_errorCode
sjme_scritchui_core_componentSetVisibleListener(
783 sjme_attrInNotNull sjme_scritchui inState
,
784 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
785 SJME_SCRITCHUI_SET_LISTENER_ARGS(visible
))
787 if (inState
== NULL
|| inComponent
== NULL
)
788 return SJME_ERROR_NULL_ARGUMENTS
;
790 return inState
->intern
->setSimpleListener(
792 (sjme_scritchui_listener_void
*)&SJME_SCRITCHUI_LISTENER_USER(
793 inComponent
, visible
),
794 (sjme_scritchui_voidListenerFunc
)inListener
,
798 sjme_errorCode
sjme_scritchui_core_componentSize(
799 sjme_attrInNotNull sjme_scritchui inState
,
800 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
801 sjme_attrOutNullable sjme_jint
* outWidth
,
802 sjme_attrOutNullable sjme_jint
* outHeight
)
804 if (inState
== NULL
|| inComponent
== NULL
||
805 (outWidth
== NULL
&& outHeight
== NULL
))
806 return SJME_ERROR_NULL_ARGUMENTS
;
809 if (inState
->impl
->componentSize
== NULL
)
810 return SJME_ERROR_NULL_ARGUMENTS
;
813 return inState
->impl
->componentSize(inState
, inComponent
,
814 outWidth
, outHeight
);
817 sjme_errorCode
sjme_scritchui_core_intern_getPaintable(
818 sjme_attrInNotNull sjme_scritchui inState
,
819 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
820 sjme_attrInOutNotNull sjme_scritchui_uiPaintable
* outPaintable
)
822 sjme_scritchui_uiPaintable paint
;
824 if (inState
== NULL
|| inComponent
== NULL
|| outPaintable
== NULL
)
825 return SJME_ERROR_NULL_ARGUMENTS
;
827 /* Only certain types can be painted on. */
828 switch (inComponent
->common
.type
)
830 case SJME_SCRITCHUI_TYPE_PANEL
:
831 paint
= &((sjme_scritchui_uiPanel
)inComponent
)->paint
;
835 return SJME_ERROR_INVALID_ARGUMENT
;
839 *outPaintable
= paint
;
840 return SJME_ERROR_NONE
;
843 sjme_errorCode
sjme_scritchui_core_intern_initCommon(
844 sjme_attrInNotNull sjme_scritchui inState
,
845 sjme_attrInNotNull sjme_scritchui_uiCommon inCommon
,
846 sjme_attrInValue sjme_jboolean postCreate
,
847 sjme_attrInRange(0, SJME_NUM_SCRITCHUI_UI_TYPES
)
848 sjme_scritchui_uiType uiType
)
850 sjme_errorCode error
;
851 sjme_alloc_link
* link
;
853 if (inState
== NULL
|| inCommon
== NULL
)
854 return SJME_ERROR_NULL_ARGUMENTS
;
856 /* Post-initialize? */
859 /* Currently nothing. */
862 /* Pre-initialize? */
865 /* Must be directly linked. */
867 if (sjme_error_is(error
= sjme_alloc_getLink(inCommon
,
868 &link
)) || link
== NULL
)
869 return sjme_error_defaultOr(error
,
870 SJME_ERROR_INVALID_LINK
);
872 /* Must be weak referenced. */
873 if (link
->weak
== NULL
)
874 return SJME_ERROR_NOT_WEAK_REFERENCE
;
876 /* Type must be valid. */
877 if (uiType
<= SJME_SCRITCHUI_TYPE_RESERVED
||
878 uiType
>= SJME_NUM_SCRITCHUI_UI_TYPES
)
879 return SJME_ERROR_INVALID_ARGUMENT
;
881 /* Set base properties. */
882 inCommon
->state
= inState
;
883 inCommon
->type
= uiType
;
887 return SJME_ERROR_NONE
;
890 sjme_errorCode
sjme_scritchui_core_intern_initComponent(
891 sjme_attrInNotNull sjme_scritchui inState
,
892 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
893 sjme_attrInValue sjme_jboolean postCreate
,
894 sjme_attrInRange(0, SJME_NUM_SCRITCHUI_UI_TYPES
)
895 sjme_scritchui_uiType uiType
)
897 sjme_errorCode error
;
898 sjme_scritchui_uiPaintable paint
;
899 sjme_scritchui_uiContainer container
;
901 if (inState
== NULL
|| inComponent
== NULL
)
902 return SJME_ERROR_NULL_ARGUMENTS
;
904 /* Common initialize. */
905 if (sjme_error_is(error
= inState
->intern
->initCommon(
906 inState
, &inComponent
->common
, postCreate
, uiType
)))
907 return sjme_error_default(error
);
909 /* Post-initialize? */
912 /* Install activate listener for activation events. */
913 if (inState
->impl
->componentSetActivateListener
!= NULL
)
915 if (sjme_error_is(error
=
916 inState
->impl
->componentSetActivateListener(inState
,
918 sjme_scritchui_core_baseActivateListener
,
920 return sjme_error_default(error
);
924 /* Still set the activation listener because there might be */
925 /* implicit soft activates. */
926 SJME_SCRITCHUI_LISTENER_CORE(inComponent
, activate
)
927 .callback
= sjme_scritchui_core_baseActivateListener
;
930 /* Install size listener to emit repaints on resize. */
931 if (inState
->impl
->componentSetSizeListener
!= NULL
)
932 if (sjme_error_is(error
=
933 inState
->impl
->componentSetSizeListener(inState
,
935 sjme_scritchui_core_baseSizeListener
,
937 return sjme_error_default(error
);
939 /* Set base visibility listener. */
940 if (inState
->impl
->componentSetVisibleListener
!= NULL
)
942 if (sjme_error_is(error
=
943 inState
->impl
->componentSetVisibleListener(inState
,
945 sjme_scritchui_core_baseVisibleListener
,
947 return sjme_error_default(error
);
951 /* If there is no native support for listeners, still set it */
952 /* as we will handle visibility ourselves manually. */
953 SJME_SCRITCHUI_LISTENER_CORE(inComponent
, visible
)
954 .callback
= sjme_scritchui_core_baseVisibleListener
;
957 /* Common paintable base initialization. */
959 if (!sjme_error_is(error
= inState
->intern
->getPaintable(
960 inState
, inComponent
, &paint
)) &&
965 /* Common container base initialization. */
967 if (!sjme_error_is(error
= inState
->intern
->getContainer(
968 inState
, inComponent
, &container
)) &&
974 /* Pre-initialize? */
977 /* Make up a string based ID for the component. */
978 snprintf(inComponent
->strId
,
979 SJME_SCRITCHUI_UI_COMPONENT_ID_STRLEN
- 1,
980 "sjme%p", inComponent
);
984 return SJME_ERROR_NONE
;
987 sjme_errorCode
sjme_scritchui_core_intern_setSimpleListener(
988 sjme_attrInNotNull sjme_scritchui inState
,
989 sjme_attrInNotNull sjme_scritchui_listener_void
* infoAny
,
990 SJME_SCRITCHUI_SET_LISTENER_ARGS(void))
992 if (inState
== NULL
|| infoAny
== NULL
)
993 return SJME_ERROR_NULL_ARGUMENTS
;
995 /* Set new callback and copy any front-end data as needed. */
996 infoAny
->callback
= inListener
;
997 if (inListener
!= NULL
&& copyFrontEnd
!= NULL
)
998 memmove(&infoAny
->frontEnd
, copyFrontEnd
,
999 sizeof(*copyFrontEnd
));
1001 /* Clear old front end data for the listener if it was cleared. */
1002 if (inListener
== NULL
)
1003 memset(&infoAny
->frontEnd
, 0, sizeof(infoAny
->frontEnd
));
1006 return SJME_ERROR_NONE
;
1009 sjme_errorCode
sjme_scritchui_core_intern_updateVisibleComponent(
1010 sjme_attrInNotNull sjme_scritchui inState
,
1011 sjme_attrInNotNull sjme_scritchui_uiComponent inComponent
,
1012 sjme_attrInValue sjme_jboolean isVisible
)
1014 sjme_scritchui_listener_visible
* infoUser
;
1016 if (inState
== NULL
|| inComponent
== NULL
)
1017 return SJME_ERROR_NULL_ARGUMENTS
;
1019 /* There always is a core interface! */
1020 infoUser
= &SJME_SCRITCHUI_LISTENER_CORE(inComponent
, visible
);
1021 if (infoUser
->callback
!= NULL
)
1022 return infoUser
->callback(inState
, inComponent
,
1023 inComponent
->state
.isVisible
, isVisible
);
1025 /* There was no callback, so just success. */
1026 return SJME_ERROR_NONE
;