diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d04792f2..7147ff4e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -18,7 +18,7 @@ libui uses K&R C formatting rules for overall code structure: spaces after keywo
Use hard tabs, NOT spaces, for indentation. I use a proportional-width font and my text editor doesn't set tabs to a multiple of the space width, so I *will* be able to tell. If you use a fixed-width font, I suggest setting a tab width of 4 spaces per tab, but don't put diagrams in comments with hard tabs, because not everyone does this.
-Expressions should have a spce around binary operators, and use parentheses where it would help humans gather the meaning of an expression, regardless of whether a computer could tell what is correct.
+Expressions should have a space around binary operators, and use parentheses where it would help humans gather the meaning of an expression, regardless of whether a computer could tell what is correct.
When breaking expressions into multiple lines, always break *after* an operator, such as `,` or `&&`.
@@ -40,6 +40,10 @@ Acronyms should **NOT** be mixed-case. `http` for the first word in a camel-case
(TODO I am writing an API documentation tool; once that becomes stable enough I can talk about documenting libui properly. You'll see vestiges of it throughout ui.h, though.)
+### Other commenting
+
+(TODO write this part)
+
### Compatibility
libui takes backward compatibility seriously. Your code should not break the current compatibility requirements. All platforms provide a series of macros, defined in the various `uipriv_*.h` files (or `winapi.hpp` on Windows), that specify the minimum required version. If you find yourself needing to remove these or ignore resultant warnings or errors, you're probably breaking compatibility.
diff --git a/README.md b/README.md
index ca2c5ac0..443c3bb2 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ This README is being written.
## Announcements
* **TODO**
- * Introduced an all-new formatted text API that allows you to process formatted text in ways that the old API wouldn't allow. You can read on the whole API [here](TODO).
+ * Introduced an all-new formatted text API that allows you to process formatted text in ways that the old API wouldn't allow. You can read on the whole API [here](TODO). There are also two new examples for this new api: `drawtext` (which shows the whole API at a glance) and `opentype` (which focuses on OpenType features).
* Also introduced a formal set of contribution guidelines, see `CONTRIBUTING.md` for details.
* **27 November 2016**
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index e011db23..f2289d21 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -42,9 +42,17 @@ _add_example(drawtext
target_include_directories(drawtext
PRIVATE drawtext)
+_add_example(opentype
+ opentype/main.c
+ ${_EXAMPLE_RESOURCES_RC}
+)
+target_include_directories(opentype
+ PRIVATE opentype)
+
add_custom_target(examples
DEPENDS
controlgallery
histogram
cpp-multithread
- drawtext)
+ drawtext
+ opentype)
diff --git a/examples/opentype/main.c b/examples/opentype/main.c
new file mode 100644
index 00000000..00c3f451
--- /dev/null
+++ b/examples/opentype/main.c
@@ -0,0 +1,173 @@
+// 10 june 2017
+#include
+#include
+#include
+#include
+#include "../../ui.h"
+
+uiWindow *mainwin;
+uiFontButton *fontButton;
+uiEntry *textEntry;
+uiCheckbox *nullFeatures;
+uiArea *area;
+
+uiAttributedString *attrstr = NULL;
+
+static void remakeAttrStr(void)
+{
+ char *text;
+ uiOpenTypeFeatures *otf;
+ uiAttributeSpec spec;
+
+ if (attrstr != NULL)
+ uiFreeAttributedString(attrstr);
+
+ text = uiEntryText(textEntry);
+ attrstr = uiNewAttributedString(text);
+ uiFreeText(text);
+
+ if (!uiCheckboxChecked(nullFeatures)) {
+ otf = uiNewOpenTypeFeatures();
+ // TODO
+ spec.Type = uiAttributeFeatures;
+ spec.Features = otf;
+ uiAttributedStringSetAttribute(attrstr, &spec,
+ 0, uiAttributedStringLen(attrstr));
+ // and uiAttributedString copied otf
+ uiFreeOpenTypeFeatures(otf);
+ }
+
+ uiAreaQueueRedrawAll(area);
+}
+
+// TODO make a variable of main()? in all programs?
+static uiAreaHandler handler;
+
+static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p)
+{
+ uiDrawTextLayout *layout;
+ uiDrawTextLayoutParams lp;
+ uiDrawFontDescriptor desc;
+
+ memset(&lp, 0, sizeof (uiDrawTextLayoutParams));
+ lp.String = attrstr;
+ uiFontButtonFont(fontButton, &desc);
+ lp.DefaultFont = &desc;
+ lp.Width = p->AreaWidth;
+ lp.Align = uiDrawTextAlignLeft;
+ layout = uiDrawNewTextLayout(&lp);
+ uiDrawText(p->Context, layout, 0, 0);
+ uiDrawFreeTextLayout(layout);
+}
+
+static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e)
+{
+ // do nothing
+}
+
+static void handlerMouseCrossed(uiAreaHandler *ah, uiArea *a, int left)
+{
+ // do nothing
+}
+
+static void handlerDragBroken(uiAreaHandler *ah, uiArea *a)
+{
+ // do nothing
+}
+
+static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e)
+{
+ // reject all keys
+ return 0;
+}
+
+static int onClosing(uiWindow *w, void *data)
+{
+ // TODO change the others to be like this? (the others destroy here rather than later)
+ // TODO move this below uiQuit()?
+ uiControlHide(uiControl(w));
+ uiQuit();
+ return 0;
+}
+
+static int shouldQuit(void *data)
+{
+ uiControlDestroy(uiControl(mainwin));
+ return 1;
+}
+
+int main(void)
+{
+ uiInitOptions o;
+ const char *err;
+ uiGrid *grid;
+ uiBox *vbox;
+
+ handler.Draw = handlerDraw;
+ handler.MouseEvent = handlerMouseEvent;
+ handler.MouseCrossed = handlerMouseCrossed;
+ handler.DragBroken = handlerDragBroken;
+ handler.KeyEvent = handlerKeyEvent;
+
+ memset(&o, 0, sizeof (uiInitOptions));
+ err = uiInit(&o);
+ if (err != NULL) {
+ fprintf(stderr, "error initializing ui: %s\n", err);
+ uiFreeInitError(err);
+ return 1;
+ }
+
+ uiOnShouldQuit(shouldQuit, NULL);
+
+ // TODO 800x600? the size of the GTK+ example?
+ mainwin = uiNewWindow("libui OpenType Features Example", 640, 480, 1);
+ uiWindowSetMargined(mainwin, 1);
+ uiWindowOnClosing(mainwin, onClosing, NULL);
+
+ grid = uiNewGrid();
+ uiGridSetPadded(grid, 1);
+ uiWindowSetChild(mainwin, uiControl(grid));
+
+ fontButton = uiNewFontButton();
+ uiGridAppend(grid, uiControl(fontButton),
+ 0, 0, 1, 1,
+ // TODO are these Y values correct?
+ 0, uiAlignFill, 0, uiAlignCenter);
+
+ textEntry = uiNewEntry();
+ uiEntrySetText(textEntry, "afford afire aflight");
+ uiGridAppend(grid, uiControl(textEntry),
+ 1, 0, 1, 1,
+ // TODO are these Y values correct too?
+ // TODO add a baseline align? or a form align?
+ 1, uiAlignFill, 0, uiAlignCenter);
+
+ vbox = uiNewVerticalBox();
+ uiBoxSetPadded(vbox, 1);
+ uiGridAppend(grid, uiControl(vbox),
+ 0, 1, 1, 1,
+ 0, uiAlignFill, 1, uiAlignFill);
+
+ nullFeatures = uiNewCheckbox("NULL uiOpenTypeFeatures");
+ uiBoxAppend(vbox, uiControl(nullFeatures), 0);
+
+ // TODO separator (if other stuff isn't a tab)
+
+ // TODO other stuff
+
+ area = uiNewArea(&handler);
+ uiGridAppend(grid, uiControl(area),
+ 1, 1, 1, 1,
+ 1, uiAlignFill, 1, uiAlignFill);
+
+ // and set up the initial draw
+ remakeAttrStr();
+
+ uiControlShow(uiControl(mainwin));
+ uiMain();
+
+ uiControlDestroy(uiControl(mainwin));
+ uiFreeAttributedString(attrstr);
+ uiUninit();
+ return 0;
+}