ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
Panel.c
Go to the documentation of this file.
1/*
2 * ObjectivelyMVC: Object oriented MVC framework for SDL3 and C.
3 * Copyright (C) 2014 Jay Dolan <jay@jaydolan.com>
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 */
23
24#include <assert.h>
25
26#include "Panel.h"
27
28#include "../Assets/resize.png.h"
29
30static Image *_resize;
31
32#define _Class _Panel
33
34#pragma mark - Object
35
39static void dealloc(Object *self) {
40
41 Panel *this = (Panel *) self;
42
43 release(this->accessoryView);
44 release(this->contentView);
45 release(this->resizeHandle);
46 release(this->stackView);
47
48 super(Object, self, dealloc);
49}
50
51#pragma mark - View
52
56static bool acceptsKeyResponder(const View *self) {
57 return false;
58}
59
63static void applyStyle(View *self, const Style *style) {
64
65 super(View, self, applyStyle, style);
66
67 Panel *this = (Panel *) self;
68
69 const Inlet inlets[] = MakeInlets(
70 MakeInlet("draggable", InletTypeBool, &this->isDraggable, NULL),
71 MakeInlet("resizable", InletTypeBool, &this->isResizable, NULL)
72 );
73
74 $(self, bind, inlets, (Dictionary *) style->attributes);
75}
76
80static void awakeWithDictionary(View *self, const Dictionary *dictionary) {
81
82 super(View, self, awakeWithDictionary, dictionary);
83
84 Panel *this = (Panel *) self;
85
86 const Inlet inlets[] = MakeInlets(
87 MakeInlet("accessoryView", InletTypeView, &this->accessoryView, NULL),
88 MakeInlet("contentView", InletTypeView, &this->contentView, NULL),
89 MakeInlet("stackView", InletTypeView, &this->stackView, NULL)
90 );
91
92 $(self, bind, inlets, dictionary);
93}
94
98static View *init(View *self) {
99 return (View *) $((Panel *) self, initWithFrame, NULL);
100}
101
105static void layoutSubviews(View *self) {
106
107 const Panel *this = (Panel *) self;
108
109 const SDL_Size size = $(this, contentSize);
110
111 $((View *) this->contentView, resize, &size);
112 $((View *) this->contentView, layoutIfNeeded);
113
114 super(View, self, layoutSubviews);
115
116 View *resizeHandle = (View *) this->resizeHandle;
117
118 resizeHandle->frame.x = self->frame.w - resizeHandle->frame.w;
119 resizeHandle->frame.y = self->frame.h - resizeHandle->frame.h;
120
121 resizeHandle->hidden = !this->isResizable;
122}
123
124#pragma mark - Control
125
129static bool captureEvent(Control *self, const SDL_Event *event) {
130
131 Panel *this = (Panel *) self;
132
133 if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
134 if ($((View *) this->resizeHandle, didReceiveEvent, event)) {
135 if (this->isResizable && !this->isDragging) {
136 this->isResizing = true;
137 }
138 return true;
139 }
140 }
141
142 if (event->type == SDL_EVENT_MOUSE_MOTION) {
143
144 if (event->motion.state & SDL_BUTTON_LEFT) {
146
147 if (this->isResizing || this->isDraggable) {
148
149 // Motion is reported in points, which are fractional on high-density
150 // displays. Carry the sub-point remainder between events so the integer
151 // deltas applied to the frame sum exactly to the cursor's movement,
152 // rather than truncating away a fraction each event and drifting behind.
153 this->gestureResidual.x += event->motion.xrel;
154 this->gestureResidual.y += event->motion.yrel;
155
156 const int dx = (int) this->gestureResidual.x;
157 const int dy = (int) this->gestureResidual.y;
158
159 this->gestureResidual.x -= dx;
160 this->gestureResidual.y -= dy;
161
162 if (this->isResizing) {
163 SDL_Size size = $((View *) self, size);
164
165 size.w += dx;
166 size.h += dy;
167
168 $((View *) self, resize, &size);
169
170 } else {
171 this->isDragging = true;
172
173 self->view.frame.x += dx;
174 self->view.frame.y += dy;
175 }
176
177 return true;
178 }
179 } else {
180 self->state &= ~ControlStateHighlighted;
181 this->isResizing = this->isDragging = false;
182 this->gestureResidual.x = this->gestureResidual.y = 0.f;
183 }
184 }
185
186 return super(Control, self, captureEvent, event);
187}
188
189#pragma mark - Panel
190
195static SDL_Size contentSize(const Panel *self) {
196
197 const View *accessoryView = (View *) self->accessoryView;
198 const View *contentView = (View *) self->contentView;
199
200 SDL_Size size = $(contentView, sizeThatContains);
201
202 if (accessoryView->hidden == false) {
203 const SDL_Size accessorySize = $(accessoryView, sizeThatContains);
204 size.h -= accessorySize.h + self->stackView->spacing;
205 }
206
207 return size;
208}
209
214static Panel *initWithFrame(Panel *self, const SDL_Rect *frame) {
215
216 self = (Panel *) super(Control, self, initWithFrame, frame);
217 if (self) {
218 View *this = (View *) self;
219
220 self->isDraggable = true;
221 self->isResizable = true;
222
223 self->stackView = $(alloc(StackView), initWithFrame, NULL);
224 assert(self->stackView);
225
226 $(this, addSubview, (View *) self->stackView);
227
228 self->contentView = $(alloc(StackView), initWithFrame, NULL);
229 assert(self->contentView);
230
231 $((View *) self->contentView, addClassName, "contentView");
232 $((View *) self->contentView, addClassName, "container");
233
235
236 $((View *) self->stackView, addSubview, (View *) self->contentView);
237
238 self->accessoryView = $(alloc(StackView), initWithFrame, NULL);
239 assert(self->accessoryView);
240
241 $((View *) self->accessoryView, addClassName, "accessoryView");
242 $((View *) self->accessoryView, addClassName, "container");
243
244 self->accessoryView->view.hidden = true;
245
246 $((View *) self->stackView, addSubview, (View *) self->accessoryView);
247
248 self->resizeHandle = $(alloc(ImageView), initWithImage, _resize);
249 assert(self->resizeHandle);
250
251 self->resizeHandle->view.alignment = ViewAlignmentInternal;
252
253 self->resizeHandle->view.frame.w = DEFAULT_PANEL_RESIZE_HANDLE_SIZE;
254 self->resizeHandle->view.frame.h = DEFAULT_PANEL_RESIZE_HANDLE_SIZE;
255
256 $((View *) self, addSubview, (View *) self->resizeHandle);
257 }
258
259 return self;
260}
261
262#pragma mark - Class lifecycle
263
267static void initialize(Class *clazz) {
268
269 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
270
271 ((ViewInterface *) clazz->interface)->acceptsKeyResponder = acceptsKeyResponder;
272 ((ViewInterface *) clazz->interface)->applyStyle = applyStyle;
273 ((ViewInterface *) clazz->interface)->awakeWithDictionary = awakeWithDictionary;
274 ((ViewInterface *) clazz->interface)->init = init;
275 ((ViewInterface *) clazz->interface)->layoutSubviews = layoutSubviews;
276
277 ((ControlInterface *) clazz->interface)->captureEvent = captureEvent;
278
279 ((PanelInterface *) clazz->interface)->contentSize = contentSize;
280 ((PanelInterface *) clazz->interface)->initWithFrame = initWithFrame;
281
282 _resize = $(alloc(Image), initWithBytes, resize_png, resize_png_len - 1);
283}
284
288static void destroy(Class *clazz) {
289 release(_resize);
290}
291
296Class *_Panel(void) {
297 static Class *clazz;
298 static Once once;
299
300 do_once(&once, {
301 clazz = _initialize(&(const ClassDef) {
302 .name = "Panel",
303 .superclass = _Control(),
304 .instanceSize = sizeof(Panel),
305 .interfaceOffset = offsetof(Panel, interface),
306 .interfaceSize = sizeof(PanelInterface),
308 .destroy = destroy,
309 });
310 });
311
312 return clazz;
313}
314
315#undef _Class
316
static Button * initWithImage(Button *self, Image *image)
Definition Button.c:161
Class * _Control(void)
Definition Control.c:391
@ ControlStateHighlighted
Definition Control.h:67
static Image * initWithBytes(Image *self, const uint8_t *bytes, size_t length)
Definition Image.c:88
static SDL_Size size(const Image *self)
Definition Image.c:181
static void addSubview(View *self, View *subview)
Definition PageView.c:49
static void destroy(Class *clazz)
Definition Panel.c:288
static SDL_Size contentSize(const Panel *self)
Definition Panel.c:195
static bool acceptsKeyResponder(const View *self)
Definition Panel.c:56
static View * init(View *self)
Definition Panel.c:98
static void awakeWithDictionary(View *self, const Dictionary *dictionary)
Definition Panel.c:80
static void applyStyle(View *self, const Style *style)
Definition Panel.c:63
static Image * _resize
Definition Panel.c:30
static bool captureEvent(Control *self, const SDL_Event *event)
Definition Panel.c:129
static Panel * initWithFrame(Panel *self, const SDL_Rect *frame)
Definition Panel.c:214
static void dealloc(Object *self)
Definition Panel.c:39
Class * _Panel(void)
Definition Panel.c:296
static void initialize(Class *clazz)
Definition Panel.c:267
static void layoutSubviews(View *self)
Definition Panel.c:105
Draggable containers.
#define DEFAULT_PANEL_RESIZE_HANDLE_SIZE
Definition Panel.h:35
@ InletTypeBool
Definition View+JSON.h:46
@ InletTypeView
Definition View+JSON.h:139
#define MakeInlets(...)
Creates a null-termianted array of Inlets.
Definition View+JSON.h:221
#define MakeInlet(name, type, dest, data)
Creates an Inlet with the specified parameters.
Definition View+JSON.h:216
static void addClassName(View *self, const char *className)
Definition View.c:159
static void resize(View *self, const SDL_Size *size)
Definition View.c:1527
static void layoutIfNeeded(View *self)
Definition View.c:1115
static bool didReceiveEvent(const View *self, const SDL_Event *event)
Definition View.c:668
static SDL_Size sizeThatContains(const View *self)
Definition View.c:1659
@ ViewAutoresizingWidth
Definition View.h:88
@ ViewAlignmentInternal
Definition View.h:78
Controls are Views which capture and respond to events.
Definition Control.h:83
unsigned int state
The bit mask of ControlState.
Definition Control.h:104
View view
The superclass.
Definition Control.h:88
Image loading.
Definition Image.h:38
ImageViews render an Image in the context of a View hierarchy.
Definition ImageView.h:45
View view
The superclass.
Definition ImageView.h:50
Inlets enable inbound data binding of View attributes through JSON.
Definition View+JSON.h:155
Draggable and resizable container Views.
Definition Panel.h:47
bool isResizable
If true, this Panel may be resized by the user.
Definition Panel.h:84
StackView * accessoryView
The optional accessories container.
Definition Panel.h:64
bool isDraggable
If true, this Panel may be repositioned by the user.
Definition Panel.h:74
StackView * contentView
The internal container.
Definition Panel.h:69
StackViews are containers that manage the arrangement of their subviews.
Definition StackView.h:68
View view
The superclass.
Definition StackView.h:73
int spacing
The subview spacing.
Definition StackView.h:94
The Style type.
Definition Style.h:43
Dictionary * attributes
Definition Style.h:59
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition View.h:134
ViewAlignment alignment
The alignment.
Definition View.h:150
int autoresizingMask
The ViewAutoresizing bitmask.
Definition View.h:155
bool acceptsKeyResponder(const View *self)
Definition Control.c:69
layoutSubviews(View *self)
Performs layout for this View's immediate subviews.
Definition Box.c:74
SDL_Rect frame
The frame, relative to the superview.
Definition View.h:191
bool hidden
If true, this View is not drawn.
Definition View.h:196