Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ObjectivelyMVC.vs15/ObjectivelyMVC.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
<ClInclude Include="..\Sources\ObjectivelyMVC\Image.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\ImageView.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\Input.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\KeyValueTableView.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\KeyValueView.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\Label.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\Log.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\NavigationViewController.h" />
Expand All @@ -52,6 +54,8 @@
<ClInclude Include="..\Sources\ObjectivelyMVC\ProgressBar.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\Renderer.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\RGBColorPicker.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\Scrollbar.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\ScrollThumb.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\ScrollView.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\Select.h" />
<ClInclude Include="..\Sources\ObjectivelyMVC\Selector.h" />
Expand Down Expand Up @@ -98,6 +102,8 @@
<ClCompile Include="..\Sources\ObjectivelyMVC\Image.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\ImageView.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\Input.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\KeyValueTableView.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\KeyValueView.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\Label.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\NavigationViewController.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\Option.c" />
Expand All @@ -106,6 +112,8 @@
<ClCompile Include="..\Sources\ObjectivelyMVC\ProgressBar.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\Renderer.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\RGBColorPicker.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\Scrollbar.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\ScrollThumb.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\ScrollView.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\Select.c" />
<ClCompile Include="..\Sources\ObjectivelyMVC\Selector.c" />
Expand Down
24 changes: 24 additions & 0 deletions ObjectivelyMVC.vs15/ObjectivelyMVC.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@
<ClInclude Include="..\Sources\ObjectivelyMVC\Input.h">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClInclude>
<ClInclude Include="..\Sources\ObjectivelyMVC\KeyValueTableView.h">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClInclude>
<ClInclude Include="..\Sources\ObjectivelyMVC\KeyValueView.h">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClInclude>
<ClInclude Include="..\Sources\ObjectivelyMVC\Label.h">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClInclude>
Expand Down Expand Up @@ -84,6 +90,12 @@
<ClInclude Include="..\Sources\ObjectivelyMVC\RGBColorPicker.h">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClInclude>
<ClInclude Include="..\Sources\ObjectivelyMVC\Scrollbar.h">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClInclude>
<ClInclude Include="..\Sources\ObjectivelyMVC\ScrollThumb.h">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClInclude>
<ClInclude Include="..\Sources\ObjectivelyMVC\ScrollView.h">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClInclude>
Expand Down Expand Up @@ -245,6 +257,12 @@
<ClCompile Include="..\Sources\ObjectivelyMVC\Input.c">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClCompile>
<ClCompile Include="..\Sources\ObjectivelyMVC\KeyValueTableView.c">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClCompile>
<ClCompile Include="..\Sources\ObjectivelyMVC\KeyValueView.c">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClCompile>
<ClCompile Include="..\Sources\ObjectivelyMVC\Label.c">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClCompile>
Expand All @@ -269,6 +287,12 @@
<ClCompile Include="..\Sources\ObjectivelyMVC\RGBColorPicker.c">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClCompile>
<ClCompile Include="..\Sources\ObjectivelyMVC\Scrollbar.c">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClCompile>
<ClCompile Include="..\Sources\ObjectivelyMVC\ScrollThumb.c">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClCompile>
<ClCompile Include="..\Sources\ObjectivelyMVC\ScrollView.c">
<Filter>Sources\ObjectivelyMVC</Filter>
</ClCompile>
Expand Down
3 changes: 2 additions & 1 deletion Sources/ObjectivelyMVC/Button.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ static void awakeWithDictionary(View *self, const Dictionary *dictionary) {
Button *this = (Button *) self;

const Inlet inlets[] = MakeInlets(
MakeInlet("title", InletTypeView, &this->title, NULL)
MakeInlet("title", InletTypeView, &this->title, NULL),
MakeInlet("image", InletTypeImage, &this->image->image, NULL)
);

$(self, bind, inlets, dictionary);
Expand Down
5 changes: 5 additions & 0 deletions Sources/ObjectivelyMVC/Control.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,11 @@ static void stateDidChange(Control *self) {
$(this, emitViewEvent, ViewEventBlur, NULL);
}

// A control's state pseudo-classes (:selected, :highlighted, :focused) can style
// DESCENDANTS, not just the control itself -- e.g. `Checkbox:selected ImageView`
// toggles the checkmark, `CollectionItemView:selected > .selectionOverlay`, etc.
// So a state change must re-theme the whole subtree. State changes are discrete
// user events (click/focus/select), so this is not a hot path.
$(this, invalidateStyle);

this->needsLayout = true;
Expand Down
202 changes: 202 additions & 0 deletions Sources/ObjectivelyMVC/KeyValueTableView.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/*
* ObjectivelyMVC: Object oriented MVC framework for SDL3 and C.
* Copyright (C) 2014 Jay Dolan <jay@jaydolan.com>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/

#include <assert.h>

#include "KeyValueTableView.h"

#define _Class _KeyValueTableView

#pragma mark - View

/**
* @see View::applyStyle(View *, const Style *)
*/
static void applyStyle(View *self, const Style *style) {

super(View, self, applyStyle, style);

KeyValueTableView *this = (KeyValueTableView *) self;

const Inlet inlets[] = MakeInlets(
MakeInlet("-key-width", InletTypeInteger, &this->keyWidth, NULL),
MakeInlet("-value-width", InletTypeInteger, &this->valueWidth, NULL)
);

$(self, bind, inlets, (Dictionary *) style->attributes);

self->needsLayout = true;
}

/**
* @see View::init(View *)
*/
static View *init(View *self) {
return (View *) $((KeyValueTableView *) self, initWithFrame, NULL);
}

/**
* @see View::layoutSubviews(View *)
*/
static void layoutSubviews(View *self) {

KeyValueTableView *this = (KeyValueTableView *) self;

// Push the table's fixed columns into every row so all rows share one set of
// column widths. setColumnWidths only writes when a clamp actually changes, so
// once the rows match this is a no-op -- it cannot churn the layout. Using the
// fixed -value-width (not a bounds-derived fill) avoids a re-layout feedback
// loop with the global `TextView { min-width }` floor.
const Array *subviews = (Array *) self->subviews;
for (size_t i = 0; i < subviews->count; i++) {
KeyValueView *row = $(subviews, objectAtIndex, i);

if (row->keyWidth != this->keyWidth || row->valueWidth != this->valueWidth) {
$(row, setColumnWidths, this->keyWidth, this->valueWidth);
}
}

super(View, self, layoutSubviews);
}

#pragma mark - KeyValueTableView

/**
* @fn KeyValueTableView *KeyValueTableView::initWithFrame(KeyValueTableView *self, const SDL_Rect *frame)
* @memberof KeyValueTableView
*/
static KeyValueTableView *initWithFrame(KeyValueTableView *self, const SDL_Rect *frame) {

self = (KeyValueTableView *) super(StackView, self, initWithFrame, frame);
if (self) {

$((View *) self, addClassName, "keyValueTable");

self->stackView.axis = StackViewAxisVertical;

// A small gap between rows so the table does not feel cramped.
self->stackView.spacing = 6;

// Grow in height with the rows; fill the parent's width so the table claims
// the box. CSS may override either of these via applyStyle.
((View *) self)->autoresizingMask = ViewAutoresizingContain | ViewAutoresizingWidth;

// Default columns so rows have widths before the stylesheet's -key-width /
// -value-width are applied; CSS overrides these in applyStyle.
self->keyWidth = 140;
self->valueWidth = 280;
}

return self;
}

/**
* @fn KeyValueView *KeyValueTableView::addRow(KeyValueTableView *self, View *key, View *value)
* @memberof KeyValueTableView
*/
static KeyValueView *addRow(KeyValueTableView *self, View *key, View *value) {

KeyValueView *row = $(alloc(KeyValueView), initWithFrame, NULL);
assert(row);

if (key) {
$(row, setKey, key);
}

if (value) {
$(row, setValue, value);
}

$(row, setColumnWidths, self->keyWidth, self->valueWidth);

$((View *) self, addSubview, (View *) row);
release(row);

((View *) self)->needsLayout = true;

return row;
}

/**
* @fn void KeyValueTableView::addRowView(KeyValueTableView *self, KeyValueView *row)
* @memberof KeyValueTableView
*/
static void addRowView(KeyValueTableView *self, KeyValueView *row) {

$(row, setColumnWidths, self->keyWidth, self->valueWidth);

$((View *) self, addSubview, (View *) row);

((View *) self)->needsLayout = true;
}

/**
* @fn void KeyValueTableView::removeAllRows(KeyValueTableView *self)
* @memberof KeyValueTableView
*/
static void removeAllRows(KeyValueTableView *self) {

$((View *) self, removeAllSubviews);
((View *) self)->needsLayout = true;
}

#pragma mark - Class lifecycle

/**
* @see Class::initialize(Class *)
*/
static void initialize(Class *clazz) {

((ViewInterface *) clazz->interface)->applyStyle = applyStyle;
((ViewInterface *) clazz->interface)->init = init;
((ViewInterface *) clazz->interface)->layoutSubviews = layoutSubviews;

((KeyValueTableViewInterface *) clazz->interface)->initWithFrame = initWithFrame;
((KeyValueTableViewInterface *) clazz->interface)->addRow = addRow;
((KeyValueTableViewInterface *) clazz->interface)->addRowView = addRowView;
((KeyValueTableViewInterface *) clazz->interface)->removeAllRows = removeAllRows;
}

/**
* @fn Class *KeyValueTableView::_KeyValueTableView(void)
* @memberof KeyValueTableView
*/
Class *_KeyValueTableView(void) {
static Class *clazz;
static Once once;

do_once(&once, {
clazz = _initialize(&(const ClassDef) {
.name = "KeyValueTableView",
.superclass = _StackView(),
.instanceSize = sizeof(KeyValueTableView),
.interfaceOffset = offsetof(KeyValueTableView, interface),
.interfaceSize = sizeof(KeyValueTableViewInterface),
.initialize = initialize,
});
});

return clazz;
}

#undef _Class
Loading
Loading